[
  {
    "path": ".gitattributes",
    "content": "# LFS configuration for images from the wiki\n*.png filter=lfs diff=lfs merge=lfs -text\n\n# Exclude LFS-tracked files from the tarball\n/docs/wiki/img/ export-ignore\n\n# exclude .gitattributes itself from the tarball\n.gitattributes export-ignore\n\n# tip: can be tested using\n# git archive --format=tar.gz --output=source.tar.gz HEAD && \\\n# tar tfvz source.tar.gz | grep -e '.png' -e '.gitattributes'\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: YaLTeR\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Report a bug or a crash\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n<!-- Please describe the issue here at the top, then fill in the system information below. -->\n\n<!-- Attaching your full niri config can help diagnose the problem. -->\n<details><summary>Config</summary>\n\n```kdl\ninsert config here\n```\n\n</details> \n\n<!--\nIf you have a problem with a specific app, please verify that it is running on Wayland, rather than X11. An easy way is to run xeyes and mouse over the app: xeyes will be able to \"see\" only X11 windows.\n\nYou can also check what process the window PID belongs to:\n\n$ readlink /proc/$(niri msg --json pick-window | jq .pid)/exe\n\nIf this points to xwayland-satellite, then it's an X11 window.\n\nPlease report issues with X11 apps to xwayland-satellite instead of niri: https://github.com/Supreeeme/xwayland-satellite/issues\n-->\n\n### System Information\n\n<!-- Paste the output of `niri -V`, e.g. niri 25.02 (b94a5db) -->\n* niri version: \n\n<!-- Write your distribution, e.g. Fedora 40 Silverblue -->\n* Distro: \n\n<!-- Write your GPU vendor and model, e.g. AMD RX 6700M -->\n* GPU: \n\n<!-- Write your CPU vendor and model, e.g. AMD Ryzen 7 6800H -->\n* CPU:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: Feature request\n    url: https://github.com/niri-wm/niri/discussions/new?category=ideas\n    about: Ideas for new features and functionality (start a Discussion)\n  - name: Ask a question\n    url: https://github.com/niri-wm/niri/discussions/new?category=q-a\n    about: Question about niri (start a Discussion)\n  - name: Matrix room\n    url: https://matrix.to/#/#niri:matrix.org\n    about: Chat about niri with other users\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"cargo\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      smithay:\n        patterns:\n          - \"smithay\"\n          - \"smithay-drm-extras\"\n      rust-dependencies:\n        update-types:\n          - \"minor\"\n          - \"patch\"\n    cooldown:\n      default-days: 7\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    cooldown:\n      default-days: 7\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 0 1 * *' # Monthly\n\nenv:\n  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\n  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\n  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\n  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\n\njobs:\n  test:\n    strategy:\n      fail-fast: false\n\n    name: test\n    runs-on: ubuntu-24.04\n    # FIXME: remove once it's available in runs-on.\n    # This is necessary for libwayland-server v1.23.\n    container: ubuntu:26.04\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install dependencies\n        run: |\n          apt-get update -y\n          apt-get install -y ${{ env.DEPS_APT }}\n\n      - uses: dtolnay/rust-toolchain@stable\n\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Build tests\n        run: cargo test --no-run --all --exclude niri-visual-tests\n\n      - name: Test\n        run: cargo test --all --exclude niri-visual-tests -- --nocapture\n\n  build:\n    strategy:\n      fail-fast: false\n\n    name: check feature combinations\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get update -y\n          sudo apt-get install -y ${{ env.DEPS_APT }}\n\n      - uses: dtolnay/rust-toolchain@stable\n\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Check (no default features)\n        run: cargo check --no-default-features\n\n      - name: Check (just dbus)\n        run: cargo check --no-default-features --features dbus\n\n      - name: Check (just systemd)\n        run: cargo check --no-default-features --features systemd\n\n      - name: Check (just dinit)\n        run: cargo check --no-default-features --features dinit\n\n      - name: Check (just xdp-gnome-screencast)\n        run: cargo check --no-default-features --features xdp-gnome-screencast\n\n      - name: Check\n        run: cargo check\n\n      - name: Build (with profiling)\n        run: cargo build --features profile-with-tracy\n\n  build-musl:\n    strategy:\n      fail-fast: false\n\n    name: alpine musl\n    runs-on: ubuntu-24.04\n    container: alpine:3\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install Deps\n        run: apk add --no-cache ${{ env.DEPS_APK }}\n\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Build\n        run: cargo build --no-default-features --features dbus,xdp-gnome-screencast\n\n  # Job that runs randomized tests for a longer period of time.\n  # Also runs normal slow tests.\n  randomized-tests:\n    strategy:\n      fail-fast: false\n\n    name: randomized and slow tests\n    runs-on: ubuntu-24.04\n    # FIXME: remove once it's available in runs-on.\n    # This is necessary for libwayland-server v1.23.\n    container: ubuntu:26.04\n\n    env:\n      RUST_BACKTRACE: 1\n      RUN_SLOW_TESTS: 1\n      PROPTEST_CASES: 200000\n      PROPTEST_MAX_LOCAL_REJECTS: 200000\n      PROPTEST_MAX_GLOBAL_REJECTS: 200000\n      PROPTEST_MAX_SHRINK_ITERS: 200000\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install dependencies\n        run: |\n          apt-get update -y\n          apt-get install -y ${{ env.DEPS_APT }}\n\n      - uses: dtolnay/rust-toolchain@stable\n\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Build tests\n        run: cargo test --no-run --all --exclude niri-visual-tests --release\n\n      - name: Test\n        run: cargo test --all --exclude niri-visual-tests --release\n\n  visual-tests:\n    strategy:\n      fail-fast: false\n\n    name: visual tests\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get update -y\n          sudo apt-get install -y ${{ env.DEPS_APT }} libadwaita-1-dev\n\n      - uses: dtolnay/rust-toolchain@stable\n\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Build\n        run: cargo build --package niri-visual-tests\n\n  msrv:\n    strategy:\n      fail-fast: false\n\n    name: msrv\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get update -y\n          sudo apt-get install -y ${{ env.DEPS_APT }} libadwaita-1-dev\n\n      - uses: dtolnay/rust-toolchain@1.85.0\n\n      - uses: Swatinem/rust-cache@v2\n\n      - run: cargo check --all-targets\n\n  clippy:\n    strategy:\n      fail-fast: false\n\n    name: clippy\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get update -y\n          sudo apt-get install -y ${{ env.DEPS_APT }} libadwaita-1-dev\n\n      - uses: dtolnay/rust-toolchain@stable\n        with:\n          components: clippy\n\n      - uses: Swatinem/rust-cache@v2\n\n      - name: Run clippy\n        run: cargo clippy --all --all-targets\n\n  rustfmt:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - uses: dtolnay/rust-toolchain@nightly\n        with:\n          components: rustfmt\n\n      - name: Run rustfmt\n        run: cargo fmt --all -- --check\n\n  fedora:\n    runs-on: ubuntu-24.04\n    container: fedora:41\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install dependencies\n        run: |\n          sudo dnf update -y\n          sudo dnf install -y ${{ env.DEPS_DNF }} libadwaita-devel\n\n      - uses: Swatinem/rust-cache@v2\n      - run: cargo build --all\n\n  freebsd:\n    runs-on: ubuntu-24.04\n    env:\n      CARGO_HOME: /home/runner/work/niri/niri/cargo-home\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      # Remove man-db triggers to speed up Ubuntu upgrade by a minute or two during vmactions/freebsd-vm action run.\n      - run: |\n          sudo rm /var/lib/dpkg/info/man-db.*\n\n      - name: Build\n        uses: vmactions/freebsd-vm@v1\n        with:\n          release: \"15.0\"\n          copyback: false\n          prepare: |\n            pkg update -f\n            pkg install -y ${{ env.DEPS_PKG }}\n          run: |\n            curl -o patch-pipewire_init 'https://cgit.freebsd.org/ports/plain/x11-wm/niri/files/patch-pipewire_init?id=cadf6784d264cf780b6e0ad59bd15b831d36cf80'\n\n            export CARGO_HOME=\"$PWD/cargo-home\"\n\n            cargo fetch\n\n            ( cd $CARGO_HOME/registry/src/index.crates.io-*/; patch -p1 < $CARGO_HOME/../patch-pipewire_init; )\n\n            cargo build \\\n              --offline \\\n              --no-default-features --features dbus,xdp-gnome-screencast\n\n  nix:\n    runs-on: ubuntu-24.04\n    steps:\n      - name: Free Disk Space (Ubuntu)\n        uses: jlumbroso/free-disk-space@v1.3.1\n        with:\n          dotnet: false\n          large-packages: false\n        \n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Install Nix\n        uses: cachix/install-nix-action@v31\n        continue-on-error: true\n\n      - run: nix flake check\n        continue-on-error: true\n\n  publish-wiki:\n    if: github.event_name == 'push' && github.ref == 'refs/heads/main'\n    needs:\n      - publish-docs\n    permissions:\n      contents: write\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          lfs: true\n          show-progress: false\n\n      - uses: Andrew-Chen-Wang/github-wiki-action@b7e552d7cb0fa7f83e459012ffc6840fd87bcb83\n        with:\n          path: docs/wiki/\n\n  publish-docs:\n    if: github.event_name == 'push' && github.ref == 'refs/heads/main'\n    needs:\n      - test\n    permissions:\n      contents: write\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          lfs: true\n          show-progress: false\n\n      - name: Install uv\n        uses: astral-sh/setup-uv@v6\n        with:\n            enable-cache: true\n\n      - name: Install the project\n        run: uv sync --locked --all-extras --dev\n        working-directory: docs/\n\n      - name: Generate niri documentation\n        run: uv run mkdocs build\n        working-directory: docs/\n\n      - uses: dtolnay/rust-toolchain@stable\n\n      - name: Generate rustdoc documentation\n        run: cargo doc --no-deps -p niri-ipc\n\n      - run: mkdir -p publish/niri_ipc\n      - run: cp -r ./target/doc/* ./publish/\n      - run: cp -r ./docs/site/* ./publish/\n\n      - name: Deploy documentation\n        if: github.ref == 'refs/heads/main'\n        uses: peaceiris/actions-gh-pages@v4\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: ./publish\n          force_orphan: true\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Prepare release\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Public version'\n        required: true\n\nconcurrency:\n  group: ${{ github.workflow }}\n  cancel-in-progress: true\n\nenv:\n  RUN_SLOW_TESTS: 1\n\njobs:\n  prepare-release:\n    runs-on: ubuntu-24.04\n\n    permissions:\n      contents: write\n\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          show-progress: false\n\n      - name: Check for unreplaced \"Since:\" in the wiki\n        run: |\n          if grep --recursive 'Since: next release' wiki; then\n            exit 1\n          fi\n\n      - name: Install dependencies\n        run: |\n          sudo apt-get update -y\n          sudo apt-get install -y 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 libadwaita-1-dev\n\n      - uses: dtolnay/rust-toolchain@stable\n\n      - name: Create vendored dependencies archive\n        run: |\n          mkdir .cargo\n          cargo vendor --locked > .cargo/config.toml\n          tar cJf niri-${{ github.event.inputs.version }}-vendored-dependencies.tar.xz vendor/\n\n      - name: Build\n        run: cargo build --all --frozen --release\n\n      - name: Build tests\n        run: cargo test --no-run --all --frozen --release\n\n      - name: Test\n        run: cargo test --all --frozen --release -- --nocapture\n\n      - name: Draft release\n        uses: softprops/action-gh-release@v2\n        with:\n          draft: true\n          tag_name: v${{ github.event.inputs.version }}\n          files: niri-${{ github.event.inputs.version }}-vendored-dependencies.tar.xz\n          fail_on_unmatched_files: true\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\n/result\n\n.idea"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to niri\n\nThanks for your interest in niri!\nThe project has grown quite a bit, and we could use all help that we can.\n\nMake sure to join our Matrix chat if you have any questions or want to discuss anything: https://matrix.to/#/#niri:matrix.org\n\n## Issues and discussions\n\nThis is a good way to help many new and existing users without programming knowledge.\n\n- Answer and help people in GitHub issues and discussions.\n- Check and point out duplicate issues.\n- Check for issues that are likely application bugs (and not niri bugs).\n    - 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.\n    - 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.\n    - 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.\n    - 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`).\n    - After testing, mention where you could and couldn't reproduce, as well as the exact steps to reproduce if the issue is missing them.\n- Try to reproduce the issue on your own system and write if you could or couldn't reproduce it.\n- Upvote issues with a thumbs up reaction as you like.\n- Ideas and feature requests from new users should go to Discussions.\n\nIf your issue is a duplicate, or not a niri issue (application bug, hardware problem, configuration problem), then please close it.\n\n## Reviewing and testing pull requests\n\nWith the growing popularity, the volume of pull requests is honestly more than I can manage myself in my free time.\nI would really appreciate help with testing and reviewing them.\n\n### Testing\n\nPick a pull request you like, then build it and give it a go.\nThe [Developing niri wiki page](https://niri-wm.github.io/niri/Development:-Developing-niri) has guidance on running niri test builds.\n\nBe really thorough with your testing.\nWe're striving for polished features in niri, so point out any issues and bugs, even small ones like animation jank.\n\n- Think of weird edge cases or unexpected interactions and try them to see that they work reasonably.\n- Try to break the feature and check that it behaves well.\n- Where applicable, try different input devices: keyboard, mouse, trackpad, tablet, touchscreen.\n- Watch out for any new performance drops.\n\nFor 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.\nBe similarly thorough: test any similar or related edge cases to verify that the fix doesn't introduce any new problems.\n\nWrite your findings in the pull request: any issues you found, or if everything worked well.\nRe-test after the author updates the code to see that your issues were fixed.\n\nDon't hesitate to test even if someone else already did; very frequently different people will stumble upon different problems.\n\n### Reviewing\n\nReviewing pull requests is something I need the most help with since there are a lot of them, and it's quite time-consuming.\nAnyone 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.\n\nPick a pull request, then review its code.\n\n- Check that everything looks good, check various conditions for edge cases.\n- See if there are any scenarios the author forgot to handle.\n- Check that the code fits well into the rest of niri, follows its design and code style.\n    - 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.\n- Check for unrelated changes that may be better split into their own pull request.\n- Check that the wiki had been updated if necessary (for example, new config options were documented with examples, and have a correct Since annotation).\n\nPoint out everything you find as review comments (don't forget to submit the review).\nBe constructive and respectful; some people may be new to programming and Rust.\nAs the author addresses the comments and issues, check the code again to see that the problems were fixed.\nIf everything looks good, say that, so I know someone has reviewed the PR.\n\nAs with testing, don't hesitate to look through and comment even if someone else already had.\nExtra pairs of eyes catch more problems.\n\n## Writing pull requests\n\nWhen creating pull requests, please keep the following in mind.\n\n- 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.\n- Keep pull requests focused on a single feature or bug fix with no unrelated changes.\n- 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.\n    - When addressing PR comments, try to squash the changes straight into the relevant commits.\n    - 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.\n    - 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.\n    - 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.\n    - [git-rebase.io](https://git-rebase.io/) is a helpful guide for splitting commits and cleaning up history in git.\n- When you address a review comment, mark it as resolved.\n- Remember to [run tests](https://niri-wm.github.io/niri/Development:-Developing-niri#tests) and format the code with `cargo +nightly fmt --all`.\n- 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.\n- Test your changes by hand thoroughly, including for edge cases and weird interactions. See the Testing section above for some tips.\n- Remember to document new config options on the wiki.\n- When opening a pull request, ensure \"Allow edits from maintainers\" is enabled, so I can make final tweaks before merging.\n\n### How to get your pull request reviewed more quickly\n\n- Make it small and self-contained. Avoid mixing several unrelated changes in one PR.\n- Split the PR into small and self-contained commits. This makes it much easier to review.\n- Discuss new features, options, or behavior changes beforehand; make sure there's consensus about the design.\n- When creating the pull request, clearly write what it does, what problem it solves, how to test it.\n- Follow the rest of the advice from this document.\n\n## AI contributions\n\nIf you use LLMs for your contribution (issue, comment, pull request), then it is *your job* to check and clean up its output, just like with any other tool.\n*You* have to spend the time doing this.\nParticularly:\n\n- If I can tell that a pull request is mostly LLM-generated, then very likely this pull request will take *significantly more time and effort* than usual to review and finish. This is based on my prior review experience. Therefore, I'm not interested in such pull requests—there's always plenty of human-written ones which take priority.\n- When using an LLM to prepare an issue, the text usually has a lot of unnecessary wording and irrelevant details. Anyone looking at such an issue will quickly lose interest in reading through it (myself certainly). Clean up the text and keep only those details that actually matter.\n- When using an LLM to comment on an issue, *you* have to verify that the comment makes sense, contributes something useful, and doesn't have unnecessary repetition.\n\n\n[cosmic-comp]: https://github.com/pop-os/cosmic-comp\n[anvil]: https://github.com/Smithay/smithay/tree/master/anvil\n[xwayland-satellite]: https://github.com/Supreeeme/xwayland-satellite\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\n    \"niri-config\",\n    \"niri-ipc\",\n    \"niri-visual-tests\",\n]\n\n[workspace.package]\nversion = \"25.11.0\"\ndescription = \"A scrollable-tiling Wayland compositor\"\nauthors = [\"Ivan Molodetskikh <yalterz@gmail.com>\"]\nlicense = \"GPL-3.0-or-later\"\nedition = \"2021\"\nrepository = \"https://github.com/niri-wm/niri\"\nrust-version = \"1.85\"\n\n[workspace.dependencies]\nanyhow = \"1.0.102\"\nbitflags = \"2.11.0\"\nclap = { version = \"4.5.60\", features = [\"derive\"] }\ninsta = \"1.46.3\"\nserde = { version = \"1.0.228\", features = [\"derive\"] }\nserde_json = \"1.0.149\"\ntracing = { version = \"0.1.44\", features = [\"max_level_trace\", \"release_max_level_debug\"] }\n# 0.3.20 filters out all ANSI codes to \"fix a security issue\" while also breaking\n# everyone who relied on them for color output, with no fallback available.\n# https://github.com/tokio-rs/tracing/issues/3378\ntracing-subscriber = { version = \"=0.3.19\", features = [\"env-filter\"] }\ntracy-client = { version = \"0.18.4\", default-features = false }\n\n[workspace.dependencies.smithay]\n# version = \"0.4.1\"\ngit = \"https://github.com/Smithay/smithay.git\"\n# path = \"../smithay\"\ndefault-features = false\n\n[workspace.dependencies.smithay-drm-extras]\n# version = \"0.1.0\"\ngit = \"https://github.com/Smithay/smithay.git\"\n# path = \"../smithay/smithay-drm-extras\"\n\n[package]\nname = \"niri\"\nversion.workspace = true\ndescription.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\nreadme = \"README.md\"\nkeywords = [\"wayland\", \"compositor\", \"tiling\", \"smithay\", \"wm\"]\n\n[dependencies]\naccesskit = { version = \"0.24.0\", optional = true }\naccesskit_unix = { version = \"0.21.0\", optional = true }\nanyhow.workspace = true\narrayvec = \"0.7.6\"\nasync-channel = \"2.5.0\"\nasync-io = { version = \"2.6.0\", optional = true }\natomic = \"0.6.1\"\nbitflags.workspace = true\nbytemuck = { version = \"1.25.0\", features = [\"derive\"] }\ncalloop = { version = \"0.14.4\", features = [\"executor\", \"futures-io\", \"signals\"] }\nclap = { workspace = true, features = [\"string\"] }\nclap_complete = \"4.5.66\"\nclap_complete_nushell = \"4.5.10\"\ndirectories = \"6.0.0\"\ndrm-ffi = \"0.9.1\"\nfastrand = \"2.3.0\"\nfutures-util = { version = \"0.3.32\", default-features = false, features = [\"std\", \"io\"] }\ngit-version = \"0.3.9\"\nglam = \"0.32.1\"\ninput = { version = \"0.9.1\", features = [\"libinput_1_21\"] }\nkeyframe = { version = \"1.1.1\", default-features = false }\nlibc = \"0.2.182\"\nlibdisplay-info = \"0.3.0\"\nlog = { version = \"0.4.29\", features = [\"max_level_trace\", \"release_max_level_debug\"] }\nniri-config = { version = \"25.11.0\", path = \"niri-config\" }\nniri-ipc = { version = \"25.11.0\", path = \"niri-ipc\", features = [\"clap\"] }\nordered-float = \"5.1.0\"\npango = { version = \"0.21.5\", features = [\"v1_44\"] }\npangocairo = \"0.21.5\"\npipewire = { version = \"0.9.2\", optional = true, features = [\"v0_3_33\"] }\npng = \"0.18.1\"\nprofiling = \"1.0.17\"\nsd-notify = \"0.4.5\"\nserde.workspace = true\nserde_json.workspace = true\nsmithay-drm-extras.workspace = true\ntracing-subscriber.workspace = true\ntracing.workspace = true\ntracy-client.workspace = true\nwayland-backend = \"0.3.14\"\nwayland-scanner = \"0.31.9\"\nwayland-server = { version = \"0.31.12\", features = [\"libwayland_1_23\"] }\nxcursor = \"0.3.10\"\nzbus = { version = \"5.13.2\", optional = true }\n\n[dependencies.smithay]\nworkspace = true\nfeatures = [\n    \"backend_drm\",\n    \"backend_egl\",\n    \"backend_gbm\",\n    \"backend_libinput\",\n    \"backend_session_libseat\",\n    \"backend_udev\",\n    \"backend_winit\",\n    \"desktop\",\n    \"renderer_gl\",\n    \"renderer_pixman\",\n    \"renderer_multi\",\n    \"use_system_lib\",\n    \"wayland_frontend\",\n]\n\n[dev-dependencies]\napprox = \"0.5.1\"\ncalloop-wayland-source = \"0.4.1\"\ninsta.workspace = true\nproptest = \"1.10.0\"\nproptest-derive = { version = \"0.8.0\", features = [\"boxed_union\"] }\nrayon = \"1.11.0\"\nwayland-client = \"0.31.13\"\nxshell = \"0.2.7\"\n\n[build-dependencies]\npkg-config = \"0.3.32\"\n\n[features]\ndefault = [\"dbus\", \"systemd\", \"xdp-gnome-screencast\"]\n# Enables D-Bus support (serve various freedesktop and GNOME interfaces, accessibility tree, power button handling).\ndbus = [\"dep:zbus\", \"dep:async-io\", \"dep:accesskit\", \"dep:accesskit_unix\"]\n# Enables systemd integration (global environment, apps in transient scopes).\nsystemd = [\"dbus\"]\n# Enables screencasting support through xdg-desktop-portal-gnome.\nxdp-gnome-screencast = [\"dbus\", \"pipewire\"]\n# Enables the Tracy profiler instrumentation.\nprofile-with-tracy = [\"profiling/profile-with-tracy\", \"tracy-client/default\", \"smithay/tracy_gpu_profiling\"]\n# Enables the on-demand Tracy profiler instrumentation.\nprofile-with-tracy-ondemand = [\"profile-with-tracy\", \"tracy-client/ondemand\", \"tracy-client/manual-lifetime\"]\n# Enables Tracy allocation profiling.\nprofile-with-tracy-allocations = [\"profile-with-tracy\"]\n# Enables dinit integration (global environment).\ndinit = []\n\n[lints.clippy]\nnew_without_default = \"allow\"\n\n[profile.release]\ndebug = \"line-tables-only\"\noverflow-checks = true\nlto = \"thin\"\n\n[profile.release.package.niri-config]\n# knuffel with chomsky generates a metric ton of debuginfo.\ndebug = false\n\n[profile.dev.package]\ninsta.opt-level = 3\nsimilar.opt-level = 3\n\n[package.metadata.generate-rpm]\nversion = \"25.11\"\nassets = [\n    { source = \"target/release/niri\", dest = \"/usr/bin/\", mode = \"755\" },\n    { source = \"resources/niri-session\", dest = \"/usr/bin/\", mode = \"755\" },\n    { source = \"resources/niri.desktop\", dest = \"/usr/share/wayland-sessions/\", mode = \"644\" },\n    { source = \"resources/niri-portals.conf\", dest = \"/usr/share/xdg-desktop-portal/\", mode = \"644\" },\n    { source = \"resources/niri.service\", dest = \"/usr/lib/systemd/user/\", mode = \"644\" },\n    { source = \"resources/niri-shutdown.target\", dest = \"/usr/lib/systemd/user/\", mode = \"644\" },\n]\n[package.metadata.generate-rpm.requires]\nalacritty = \"*\"\nfuzzel = \"*\"\n\n[package.metadata.deb]\ndepends = \"alacritty, fuzzel\"\nassets = [\n    [\"target/release/niri\", \"usr/bin/\", \"755\"],\n    [\"resources/niri-session\", \"usr/bin/\", \"755\"],\n    [\"resources/niri.desktop\", \"/usr/share/wayland-sessions/\", \"644\"],\n    [\"resources/niri-portals.conf\", \"/usr/share/xdg-desktop-portal/\", \"644\"],\n    [\"resources/niri.service\", \"/usr/lib/systemd/user/\", \"644\"],\n    [\"resources/niri-shutdown.target\", \"/usr/lib/systemd/user/\", \"644\"],\n]\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\"><img alt=\"niri\" src=\"https://github.com/user-attachments/assets/07d05cd0-d5dc-4a28-9a35-51bae8f119a0\"></h1>\n<p align=\"center\">A scrollable-tiling Wayland compositor.</p>\n<p align=\"center\">\n    <a href=\"https://matrix.to/#/#niri:matrix.org\"><img alt=\"Matrix\" src=\"https://img.shields.io/badge/matrix-%23niri-blue?logo=matrix\"></a>\n    <a href=\"https://github.com/niri-wm/niri/blob/main/LICENSE\"><img alt=\"GitHub License\" src=\"https://img.shields.io/github/license/niri-wm/niri\"></a>\n    <a href=\"https://github.com/niri-wm/niri/releases\"><img alt=\"GitHub Release\" src=\"https://img.shields.io/github/v/release/niri-wm/niri?logo=github\"></a>\n</p>\n\n<p align=\"center\">\n    <a href=\"https://niri-wm.github.io/niri/Getting-Started.html\">Getting Started</a> | <a href=\"https://niri-wm.github.io/niri/Configuration%3A-Introduction.html\">Configuration</a> | <a href=\"https://github.com/niri-wm/niri/discussions/325\">Setup&nbsp;Showcase</a>\n</p>\n\n![niri with a few windows open](https://github.com/user-attachments/assets/535e6530-2f44-4b84-a883-1240a3eee6e9)\n\n## About\n\nWindows are arranged in columns on an infinite strip going to the right.\nOpening a new window never causes existing windows to resize.\n\nEvery monitor has its own separate window strip.\nWindows can never \"overflow\" onto an adjacent monitor.\n\nWorkspaces are dynamic and arranged vertically.\nEvery monitor has an independent set of workspaces, and there's always one empty workspace present all the way down.\n\nThe workspace arrangement is preserved across disconnecting and connecting monitors where it makes sense.\nWhen a monitor disconnects, its workspaces will move to another monitor, but upon reconnection they will move back to the original monitor.\n\n## Features\n\n- Built from the ground up for scrollable tiling\n- [Dynamic workspaces](https://niri-wm.github.io/niri/Workspaces.html) like in GNOME\n- An [Overview](https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995) that zooms out workspaces and windows\n- Built-in screenshot UI\n- Monitor and window screencasting through xdg-desktop-portal-gnome\n    - You can [block out](https://niri-wm.github.io/niri/Configuration%3A-Window-Rules.html#block-out-from) sensitive windows from screencasts\n    - [Dynamic cast target](https://niri-wm.github.io/niri/Screencasting.html#dynamic-screencast-target) that can change what it shows on the go\n- [Touchpad](https://github.com/niri-wm/niri/assets/1794388/946a910e-9bec-4cd1-a923-4a9421707515) and [mouse](https://github.com/niri-wm/niri/assets/1794388/8464e65d-4bf2-44fa-8c8e-5883355bd000) gestures\n- Group windows into [tabs](https://niri-wm.github.io/niri/Tabs.html)\n- Configurable layout: gaps, borders, struts, window sizes\n- [Gradient borders](https://niri-wm.github.io/niri/Configuration%3A-Layout.html#gradients) with Oklab and Oklch support\n- [Animations](https://github.com/niri-wm/niri/assets/1794388/ce178da2-af9e-4c51-876f-8709c241d95e) with support for [custom shaders](https://github.com/niri-wm/niri/assets/1794388/27a238d6-0a22-4692-b794-30dc7a626fad)\n- Live-reloading config\n- Works with [screen readers](https://niri-wm.github.io/niri/Accessibility.html)\n\n## Video Demo\n\nhttps://github.com/niri-wm/niri/assets/1794388/bce834b0-f205-434e-a027-b373495f9729\n\nAlso 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)\n\n## Status\n\nNiri is stable for day-to-day use and does most things expected of a Wayland compositor.\nMany people are daily-driving niri, and are happy to help in our [Matrix channel].\n\nGive it a try!\nFollow the instructions on the [Getting Started](https://niri-wm.github.io/niri/Getting-Started.html) page.\nHave your [waybar]s and [fuzzel]s ready: niri is not a complete desktop environment.\nAlso check out [awesome-niri], a list of niri-related links and projects.\n\nHere are some points you may have questions about:\n\n- **Multi-monitor**: yes, a core part of the design from the very start. Mixed DPI works.\n- **Fractional scaling**: yes, plus all niri UI stays pixel-perfect.\n- **NVIDIA**: seems to work fine.\n- **Floating windows**: yes, starting from niri 25.01.\n- **Input devices**: niri supports tablets, touchpads, and touchscreens.\nYou can map the tablet to a specific monitor, or use [OpenTabletDriver].\nWe have touchpad gestures, but no touchscreen gestures yet.\n- **Wlr protocols**: yes, we have most of the important ones like layer-shell, gamma-control, screencopy.\nYou can check on [wayland.app](https://wayland.app) at the bottom of each protocol's page.\n- **Performance**: while I run niri on beefy machines, I try to stay conscious of performance.\nI've seen someone use it fine on an Eee PC 900 from 2008, of all things.\n- **Xwayland**: [integrated](https://niri-wm.github.io/niri/Xwayland.html#using-xwayland-satellite) via xwayland-satellite starting from niri 25.08.\n\n## Media\n\n[niri: Making a Wayland compositor in Rust](https://youtu.be/Kmz8ODolnDg?list=PLRdS-n5seLRqrmWDQY4KDqtRMfIwU0U3T) · *December 2024*\n\nMy talk from the 2024 Moscow RustCon about niri, and how I do randomized property testing and profiling, and measure input latency.\nThe talk is in Russian, but I prepared full English subtitles that you can find in YouTube's subtitle language selector.\n\n[An interview with Ivan, the developer behind Niri](https://www.trommelspeicher.de/podcast/special_the_developer_behind_niri) · *June 2025*\n\nAn interview by a German tech podcast Das Triumvirat (in English).\nWe talk about niri development and history, and my experience building and maintaining niri.\n\n[A tour of the niri scrolling-tiling Wayland compositor](https://lwn.net/Articles/1025866/) · *July 2025*\n\nAn LWN article with a nice overview and introduction to niri.\n\n## Contributing\n\nIf you'd like to help with niri, there are plenty of both coding- and non-coding-related ways to do so.\nSee [CONTRIBUTING.md](https://github.com/niri-wm/niri/blob/main/CONTRIBUTING.md) for an overview.\n\n## Inspiration\n\nNiri is heavily inspired by [PaperWM] which implements scrollable tiling on top of GNOME Shell.\n\nOne of the reasons that prompted me to try writing my own compositor is being able to properly separate the monitors.\nBeing a GNOME Shell extension, PaperWM has to work against Shell's global window coordinate space to prevent windows from overflowing.\n\n## Tile Scrollably Elsewhere\n\nHere are some other projects which implement a similar workflow:\n\n- [PaperWM]: scrollable tiling on top of GNOME Shell.\n- [karousel]: scrollable tiling on top of KDE.\n- [scroll](https://github.com/dawsers/scroll) and [papersway]: scrollable tiling on top of sway/i3.\n- [hyprscrolling] and [hyprslidr]: scrollable tiling on top of Hyprland.\n- [PaperWM.spoon]: scrollable tiling on top of macOS.\n\n## Contact\n\nOur main communication channel is a Matrix chat, feel free to join and ask a question: https://matrix.to/#/#niri:matrix.org\n\nWe also have a community Discord server: https://discord.gg/vT8Sfjy7sx\n\n[PaperWM]: https://github.com/paperwm/PaperWM\n[waybar]: https://github.com/Alexays/Waybar\n[fuzzel]: https://codeberg.org/dnkl/fuzzel\n[awesome-niri]: https://github.com/niri-wm/awesome-niri\n[karousel]: https://github.com/peterfajdiga/karousel\n[papersway]: https://spwhitton.name/tech/code/papersway/\n[hyprscrolling]: https://github.com/hyprwm/hyprland-plugins/tree/main/hyprscrolling\n[hyprslidr]: https://gitlab.com/magus/hyprslidr\n[PaperWM.spoon]: https://github.com/mogenson/PaperWM.spoon\n[Matrix channel]: https://matrix.to/#/#niri:matrix.org\n[OpenTabletDriver]: https://opentabletdriver.net/\n"
  },
  {
    "path": "build.rs",
    "content": "fn main() {\n    println!(\"cargo:rustc-check-cfg=cfg(have_libinput_plugin_system)\");\n    if pkg_config::Config::new()\n        .atleast_version(\"1.30.0\")\n        .probe(\"libinput\")\n        .is_ok()\n    {\n        println!(\"cargo:rustc-cfg=have_libinput_plugin_system\")\n    }\n}\n"
  },
  {
    "path": "clippy.toml",
    "content": "ignore-interior-mutability = [\n    \"smithay::desktop::Window\",\n    \"smithay::output::Output\",\n    \"wayland_server::backend::ClientId\",\n]\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "site\n__pycache__"
  },
  {
    "path": "docs/hooks/remove-must-fail.py",
    "content": "from __future__ import annotations\nimport re\n\n# todo: this could be done generically, so that any \n# ```language,annotation,anything-else \n# is reduced to\n# ```language\n# which is what's supported by mkdocs/pygments\n# also note: mkdocs provides ways to highlight lines, add line numbers\n# but these are added as \n# ```language linenums=\"1\"\n# and not split by comma\ndef on_page_markdown(\n    markdown: str, *, page, config, files\n):\n    return re.sub(\n        r\",must-fail\",\n        '', markdown, flags = re.I | re.M\n    )"
  },
  {
    "path": "docs/hooks/shortcodes.py",
    "content": "# Copyright (c) 2016-2025 Martin Donath <martin.donath@squidfunk.com>\n\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to\n# deal in the Software without restriction, including without limitation the\n# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n# sell copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n# IN THE SOFTWARE.\n\nfrom __future__ import annotations\nimport re\nfrom re import Match\n\ndef on_page_markdown(\n    markdown: str, *, page, config, files\n):\n    def replace(match: Match):\n        matches = match.groups()\n        preposition, version = matches[0], matches[1]\n        return _badge_for_version(preposition, version)\n\n    return re.sub(\n        r\"<sup>(Until|Since): (.*?)</sup>\",\n        replace, markdown, flags = re.I | re.M\n    )\n\ndef _badge_for_version(preposition: str, version: str):\n    if version == \"next release\":\n        # we might fail to make real links to release notes on other cases too, but for now this is the one i've found\n        return f\"<span class=\\\"badge\\\">{preposition}: {version}</span>\"\n    else:\n        path = f\"https://github.com/niri-wm/niri/releases/tag/v{version}\"\n        return f\"<span class=\\\"badge\\\">[{preposition}: {version}]({path})</span>\"\n"
  },
  {
    "path": "docs/mkdocs.yaml",
    "content": "site_name: niri\ndocs_dir: wiki\nsite_url: https://niri-wm.github.io/niri\nrepo_url: https://github.com/niri-wm/niri\nedit_uri: edit/main/docs/wiki/\nuse_directory_urls: false\n\ntheme:\n  name: material\n  logo: _assets/icons/logo.svg\n  favicon: _assets/icons/logo.svg\n  features:\n    - navigation.instant\n    - search.suggest\n    - content.code.copy\n    - content.action.edit\n  palette:\n    - media: \"(prefers-color-scheme)\"\n      primary: custom\n      toggle:\n        icon: material/brightness-auto\n        name: Switch to light mode\n    - media: \"(prefers-color-scheme: light)\"\n      primary: custom\n      scheme: default\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n    - media: \"(prefers-color-scheme: dark)\"\n      primary: custom\n      scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to system preference\nmarkdown_extensions:\n  - github-callouts\n  - pymdownx.highlight:\n      anchor_linenums: true\n      line_spans: __span\n      pygments_lang_class: true\n  - pymdownx.inlinehilite\n  - pymdownx.magiclink\n  - pymdownx.snippets\n  - pymdownx.superfences\n  - pymdownx.keys\n  - toc:\n      permalink: '#'\nplugins:\n  - search\nhooks:\n  - hooks/shortcodes.py\n  - hooks/remove-must-fail.py\nextra_css:\n  - _assets/stylesheets/niri.css\nstrict: true\nvalidation:\n  nav:\n    omitted_files: warn\n    not_found: warn\n    absolute_links: relative_to_docs\n  links:\n    not_found: warn\n    anchors: warn\n    absolute_links: relative_to_docs\n    unrecognized_links: warn\nnot_in_nav: |\n  _Sidebar.md\n  Configuration:-Overview.md\n  README.md\n# ah, wouldn't it be nice if we could autogenerate this with wiki/_Sidebar.md\nnav:\n  - Usage:\n    - Getting Started: Getting-Started.md\n    - Example systemd Setup: Example-systemd-Setup.md\n    - Important Software: Important-Software.md\n    - Workspaces: Workspaces.md\n    - Floating Windows: Floating-Windows.md\n    - Tabs: Tabs.md\n    - Overview: Overview.md\n    - Screencasting: Screencasting.md\n    - Layer‐Shell Components: Layer‐Shell-Components.md\n    - IPC, niri msg: IPC.md\n    - Application-Specific Issues: Application-Issues.md\n    - Nvidia: Nvidia.md\n    - Xwayland: Xwayland.md\n    - Gestures: Gestures.md\n    - Fullscreen and Maximize: Fullscreen-and-Maximize.md\n    - Packaging niri: Packaging-niri.md\n    - Integrating niri: Integrating-niri.md\n    - Accessibility: Accessibility.md\n    - Name and Logo: Name-and-Logo.md\n    - FAQ: FAQ.md\n  - Configuration:\n    - Introduction: Configuration:-Introduction.md\n    - Input: Configuration:-Input.md\n    - Outputs: Configuration:-Outputs.md\n    - Key Bindings: Configuration:-Key-Bindings.md\n    - Switch Events: Configuration:-Switch-Events.md\n    - Layout: Configuration:-Layout.md\n    - Named Workspaces: Configuration:-Named-Workspaces.md\n    - Miscellaneous: Configuration:-Miscellaneous.md\n    - Window Rules: Configuration:-Window-Rules.md\n    - Layer Rules: Configuration:-Layer-Rules.md\n    - Animations: Configuration:-Animations.md\n    - Gestures: Configuration:-Gestures.md\n    - Recent Windows: Configuration:-Recent-Windows.md\n    - Debug Options: Configuration:-Debug-Options.md\n    - Include: Configuration:-Include.md\n  - Development:\n    - Design Principles: Development:-Design-Principles.md\n    - Developing niri: Development:-Developing-niri.md\n    - Documenting niri: Development:-Documenting-niri.md\n    - Fractional Layout: Development:-Fractional-Layout.md\n    - Redraw Loop: Development:-Redraw-Loop.md\n    - Animation Timing: Development:-Animation-Timing.md\n"
  },
  {
    "path": "docs/pyproject.toml",
    "content": "[project]\nname = \"niri-docs\"\nversion = \"0.1.0\"\nrequires-python = \">=3.10\"\ndependencies = [\n    \"markdown-callouts>=0.4.0\",\n    \"mkdocs-material>=9.6.15\",\n    \"pygments\",\n]\n\n# for KDL highlighting support\n# FIXME: use the official pygments package once https://github.com/pygments/pygments/pull/2936 is merged\n[tool.uv.sources]\npygments = { git = \"https://github.com/chinatsu/pygments\", rev = \"0f0b0d4da2839e1285881389155bb4605a0a6dc4\" }\n"
  },
  {
    "path": "docs/wiki/Accessibility.md",
    "content": "## Screen readers\n\n<sup>Since: 25.08</sup>\n\nNiri has basic support for screen readers (specifically, [Orca](https://orca.gnome.org)) when running as a full desktop session, i.e. you need to start niri through a display manager or through `niri-session`.\nTo avoid conflicts with an already running compositor, niri won't expose accessibility interfaces when started as a nested window, or as a plain `/usr/bin/niri` on a TTY.\n\nWe implement the `org.freedesktop.a11y.KeyboardMonitor` D-Bus interface for Orca to listen and grab keyboard keys, and we expose the main niri UI elements via [AccessKit](https://accesskit.dev).\nSpecifically, niri will announce:\n\n- workspace switching, for example it'll say \"Workspace 2\" when you switch to the second workspace;\n- the exit confirmation dialog (appears on <kbd>Super</kbd><kbd>Shift</kbd><kbd>E</kbd> by default);\n- <sup>Since: 25.11</sup> niri has an <kbd>Alt</kbd><kbd>Tab</kbd> window switcher where it will announce the selected window title;\n- entering the screenshot UI and the overview (niri will say when these are focused, nothing else for now);\n- whenever a config parse error occurs;\n- the important hotkeys list (for now, as one big announcement without tab navigation; appears on <kbd>Super</kbd><kbd>Shift</kbd><kbd>/</kbd> by default).\n\nHere's a demo video, watch with sound on.\n\n<video controls src=\"https://github.com/user-attachments/assets/afceba6f-79f1-47ec-b859-a0fcb7f8eae3\">\n\nhttps://github.com/user-attachments/assets/afceba6f-79f1-47ec-b859-a0fcb7f8eae3\n\n</video>\n\nMake sure [Xwayland](./Xwayland.md) works, then run `orca`.\nThe default config binds <kbd>Super</kbd><kbd>Alt</kbd><kbd>S</kbd> to toggle Orca, which is the standard key binding.\n\nNote that there are some limitations:\n\n- We don't have a bind to move focus to layer-shell panels. This is not hard to add, but it would be good to have some consensus or prior art with LXQt/Xfce on how exactly this should work.\n- You need to have a screen connected and enabled. Without a screen, niri won't give focus any window. This makes sense for sighted users, and I'm not entirely sure what makes the most sense for accessibility purposes (maybe, it'd be better solved with virtual monitors).\n- You need working EGL (hardware acceleration).\n- We don't have screen curtain functionality yet.\n\nIf you're shipping niri and would like to make it work better for screen readers out of the box, consider the following changes to the default niri config:\n\n- Change the default terminal from Alacritty to one that supports screen readers. For example, [GNOME Console](https://gitlab.gnome.org/GNOME/console) or [GNOME Terminal](https://gitlab.gnome.org/GNOME/gnome-terminal) should work well.\n- Change the default application launcher and screen locker to ones that support screen readers. For example, [xfce4-appfinder](https://docs.xfce.org/xfce/xfce4-appfinder/start) is an accessible launcher. Suggestions welcome! Likely, something GTK-based will work fine.\n- Add some [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup) command that plays a sound which will indicate to users that niri has finished loading.\n- Add `spawn-at-startup \"orca\"` to run Orca automatically at niri startup.\n\n## Desktop zoom\n\nThere's no built-in zoom yet, but you can use third-party utilities like [wooz](https://github.com/negrel/wooz).\n"
  },
  {
    "path": "docs/wiki/Application-Issues.md",
    "content": "### Electron applications\n\nElectron-based applications can run directly on Wayland, but it's not the default.\n\nFor Electron ≥ 39, you can use the command-line flag if the app does not default to Wayland:\n```\n--ozone-platform=wayland\n```\n\nFor Electron < 39, you can set an environment variable:\n```kdl\nenvironment {\n    ELECTRON_OZONE_PLATFORM_HINT \"auto\"\n}\n```\n\nFor Electron ≤ 28, you need to pass command-line flags to the target application:\n```\n--enable-features=UseOzonePlatform --ozone-platform-hint=auto\n```\n\nIf the application has a [desktop entry](https://specifications.freedesktop.org/menu-spec/latest/menu-add-example.html), you can put the command-line arguments into the `Exec` section.\n\n### VSCode\n\nIf you're having issues with some VSCode hotkeys, try starting `Xwayland` and setting the `DISPLAY=:0` environment variable for VSCode.\nThat is, still running VSCode with the Wayland backend, but with `DISPLAY` set to a running Xwayland instance.\nApparently, VSCode currently unconditionally queries the X server for a keymap.\n\n### JetBrains IDEs\n\nJetBrains IDEs can run directly on Wayland, but it's not the default.\n\nFor JetBrainsRuntime > 17, you can set the flag `-Dawt.toolkit.name=WLToolkit` inside of `help -> edit custom vm options -> add`.\n\n### WezTerm\n\n> [!NOTE]\n> Both of these issues seem to be fixed in the nightly build of WezTerm.\n\nThere's [a bug](https://github.com/wezterm/wezterm/issues/4708) in WezTerm that it waits for a zero-sized Wayland configure event, so its window never shows up in niri. To work around it, put this window rule in the niri config (included in the default config):\n\n```kdl\nwindow-rule {\n    match app-id=r#\"^org\\.wezfurlong\\.wezterm$\"#\n    default-column-width {}\n}\n```\n\nThis empty default column width lets WezTerm pick its own initial width which makes it show up properly.\n\nThere's [another bug](https://github.com/wezterm/wezterm/issues/6472) in WezTerm that causes it to choose a wrong size when it's in a tiled state, and prevent resizing it.\nNiri puts windows in the tiled state with [`prefer-no-csd`](./Configuration:-Miscellaneous.md#prefer-no-csd).\nSo if you hit this problem, comment out `prefer-no-csd` in the niri config and restart WezTerm.\n\n### Ghidra\n\nSome Java apps like Ghidra can show up blank under xwayland-satellite.\nTo fix this, run them with the `_JAVA_AWT_WM_NONREPARENTING=1` environment variable.\n\n### Zen Browser\n\nFor some reason, DMABUF screencasts are disabled in the Zen Browser, so screencasting doesn't work out of the box on niri.\nTo fix it, open `about:config` and set `widget.dmabuf.force-enabled` to `true`.\n\n### GTK 4 dead keys / Compose\n\nGTK 4.20 [stopped](https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8556) handling dead keys and Compose on its own on Wayland.\nTo make them work, either run an IME like IBus or Fcitx5, or set the `GTK_IM_MODULE=simple` environment variable.\n\n```kdl\nenvironment {\n    GTK_IM_MODULE \"simple\"\n}\n```\n\nNote that the niri environment config does not propagate to apps and shells started by systemd, for example to DankMaterialShell and its application launcher.\nYou can set the variable in your login shell config (i.e. `~/.bash_profile`) instead, though keep in mind that then it will be set for all compositors, not just niri.\n\n### Fullscreen games\n\nSome video games, both Linux-native and on Wine, have various issues when using non-stacking desktop environments.\nMost of these can be avoided with Valve's [gamescope](https://github.com/ValveSoftware/gamescope), for example:\n\n```sh\ngamescope -f -w 1920 -h 1080 -W 1920 -H 1080 --force-grab-cursor --backend sdl -- <game>\n```\n\nThis command will run *<game>* in 1080p fullscreen—make sure to replace the width and height values to match your desired resolution.\n`--force-grab-cursor` forces gamescope to use relative mouse movement which prevents the cursor from escaping the game's window on multi-monitor setups.\nNote that `--backend sdl` is currently also required as gamescope's default Wayland backend doesn't lock the cursor properly (possibly related to https://github.com/ValveSoftware/gamescope/issues/1711).\n\nSteam users should use gamescope through a game's [launch options](https://help.steampowered.com/en/faqs/view/7D01-D2DD-D75E-2955) by replacing the game executable with `%command%`.\nOther game launchers such as [Lutris](https://lutris.net/) have their own ways of setting gamescope options.\n\nRunning X11-based games with this method doesn't require Xwayland as gamescope creates its own Xwayland server.\nYou can run Wayland-native games as well by passing `--expose-wayland` to gamescope, therefore eliminating X11 from the equation.\n\n### Steam\n\nOn some systems, Steam will show a fully black window.\nTo fix this, navigate to Settings -> Interface (via Steam's tray icon, or by blindly finding the Steam menu at the top left of the window), then **disable** GPU accelerated rendering in web views.\nRestart Steam and it should now work fine.\n\nIf you do not want to disable GPU accelerated rendering you can instead try to pass the launch argument `-system-composer` instead.\n\nSteam notifications don't run through the standard notification daemon and show up as floating windows in the center of the screen.\nYou can move them to a more convenient location by adding a window rule in your niri config:\n\n```kdl\nwindow-rule {\n    match app-id=\"steam\" title=r#\"^notificationtoasts_\\d+_desktop$\"#\n    default-floating-position x=10 y=10 relative-to=\"bottom-right\"\n}\n```\n\n### Waybar and other GTK 3 components\n\nIf you have rounded corners on your Waybar and they show up with black pixels in the corners, then set your Waybar opacity to 0.99, which should fix it.\n\nGTK 3 seems to have a bug where it reports a surface as fully opaque even if it has rounded corners.\nThis leads to niri filling the transparent pixels inside the corners with black.\n\nSetting the surface opacity to something below 1 fixes the problem because then GTK no longer reports the surface as opaque.\n"
  },
  {
    "path": "docs/wiki/Configuration:-Animations.md",
    "content": "### Overview\n\nNiri has several animations which you can configure in the same way.\nAdditionally, you can disable or slow down all animations at once.\n\nHere's a quick glance at the available animations with their default values.\n\n```kdl\nanimations {\n    // Uncomment to turn off all animations.\n    // You can also put \"off\" into each individual animation to disable it.\n    // off\n\n    // Slow down all animations by this factor. Values below 1 speed them up instead.\n    // slowdown 3.0\n\n    // Individual animations.\n\n    workspace-switch {\n        spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001\n    }\n\n    window-open {\n        duration-ms 150\n        curve \"ease-out-expo\"\n    }\n\n    window-close {\n        duration-ms 150\n        curve \"ease-out-quad\"\n    }\n\n    horizontal-view-movement {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n\n    window-movement {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n\n    window-resize {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n\n    config-notification-open-close {\n        spring damping-ratio=0.6 stiffness=1000 epsilon=0.001\n    }\n\n    exit-confirmation-open-close {\n        spring damping-ratio=0.6 stiffness=500 epsilon=0.01\n    }\n\n    screenshot-ui-open {\n        duration-ms 200\n        curve \"ease-out-quad\"\n    }\n\n    overview-open-close {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n\n    recent-windows-close {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.001\n    }\n}\n```\n\n### Animation Types\n\nThere are two animation types: easing and spring.\nEach animation can be either an easing or a spring.\n\n#### Easing\n\nThis is a relatively common animation type that changes the value over a set duration using an interpolation curve.\n\nTo use this animation, set the following parameters:\n\n- `duration-ms`: duration of the animation in milliseconds.\n- `curve`: the easing curve to use.\n\n```kdl\nanimations {\n    window-open {\n        duration-ms 150\n        curve \"ease-out-expo\"\n    }\n}\n```\n\nCurrently, niri only supports five curves.\nYou can get a feel for them on pages like [easings.net](https://easings.net/).\n\n- `ease-out-quad` <sup>Since: 0.1.5</sup>\n- `ease-out-cubic`\n- `ease-out-expo`\n- `linear` <sup>Since: 0.1.6</sup>\n- `cubic-bezier` <sup>Since: 25.08</sup>\n    A custom [cubic Bézier curve](https://www.w3.org/TR/css-easing-1/#cubic-bezier-easing-functions). You need to set 4 numbers defining the control points of the curve, for example:\n    ```kdl\n    animations {\n        window-open {\n            // Same as CSS cubic-bezier(0.05, 0.7, 0.1, 1)\n            curve \"cubic-bezier\" 0.05 0.7 0.1 1\n        }\n    }\n    ```\n    You can tweak the cubic-bezier parameters on pages like [easings.co](https://easings.co?curve=0.05,0.7,0.1,1).\n\n#### Spring\n\nSpring animations use a model of a physical spring to animate the value.\nThey notably feel better with touchpad gestures, because they take into account the velocity of your fingers as you release the swipe.\nSprings can also oscillate / bounce at the end with the right parameters if you like that sort of thing, but they don't have to (and by default they mostly don't).\n\nDue to springs using a physical model, the animation parameters are less obvious and generally should be tuned with trial and error.\nNotably, you cannot directly set the duration.\nYou can use the [Elastic](https://flathub.org/apps/app.drey.Elastic) app to help visualize how the spring parameters change the animation.\n\nA spring animation is configured like this, with three mandatory parameters:\n\n```kdl\nanimations {\n    workspace-switch {\n        spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001\n    }\n}\n```\n\nThe `damping-ratio` goes from 0.1 to 10.0 and has the following properties:\n\n- below 1.0: underdamped spring, will oscillate in the end.\n- above 1.0: overdamped spring, won't oscillate.\n- 1.0: critically damped spring, comes to rest in minimum possible time without oscillations.\n\nHowever, even with damping ratio = 1.0, the spring animation may oscillate if \"launched\" with enough velocity from a touchpad swipe.\n\n> [!WARNING]\n> Overdamped springs currently have some numerical stability issues and may cause graphical glitches.\n> Therefore, setting `damping-ratio` above `1.0` is not recommended.\n\nLower `stiffness` will result in a slower animation more prone to oscillation.\n\nSet `epsilon` to a lower value if the animation \"jumps\" at the end.\n\n> [!TIP]\n> The spring *mass* (which you can see in Elastic) is hardcoded to 1.0 and cannot be changed.\n> Instead, change `stiffness` proportionally.\n> E.g. increasing mass by 2× is the same as decreasing stiffness by 2×.\n\n### Animations\n\nNow let's go into more detail on the animations that you can configure.\n\n#### `workspace-switch`\n\nAnimation when switching workspaces up and down, including after the vertical touchpad gesture (a spring is recommended).\n\n```kdl\nanimations {\n    workspace-switch {\n        spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001\n    }\n}\n```\n\n#### `window-open`\n\nWindow opening animation.\n\nThis one uses an easing type by default.\n\n```kdl\nanimations {\n    window-open {\n        duration-ms 150\n        curve \"ease-out-expo\"\n    }\n}\n```\n\n##### `custom-shader`\n\n<sup>Since: 0.1.6</sup>\n\nYou can write a custom shader for drawing the window during an open animation.\n\nSee [this example shader](./examples/open_custom_shader.frag) for a full documentation with several animations to experiment with.\n\nIf a custom shader fails to compile, niri will print a warning and fall back to the default, or previous successfully compiled shader.\nWhen running niri as a systemd service, you can see the warnings in the journal: `journalctl -ef /usr/bin/niri`\n\n> [!WARNING]\n>\n> Custom shaders do not have a backwards compatibility guarantee.\n> I may need to change their interface as I'm developing new features.\n\nExample: open will fill the current geometry with a solid gradient that gradually fades in.\n\n```kdl\nanimations {\n    window-open {\n        duration-ms 250\n        curve \"linear\"\n\n        custom-shader r\"\n            vec4 open_color(vec3 coords_geo, vec3 size_geo) {\n                vec4 color = vec4(0.0);\n\n                if (0.0 <= coords_geo.x && coords_geo.x <= 1.0\n                        && 0.0 <= coords_geo.y && coords_geo.y <= 1.0)\n                {\n                    vec4 from = vec4(1.0, 0.0, 0.0, 1.0);\n                    vec4 to = vec4(0.0, 1.0, 0.0, 1.0);\n                    color = mix(from, to, coords_geo.y);\n                }\n\n                return color * niri_clamped_progress;\n            }\n        \"\n    }\n}\n```\n\n#### `window-close`\n\n<sup>Since: 0.1.5</sup>\n\nWindow closing animation.\n\nThis one uses an easing type by default.\n\n```kdl\nanimations {\n    window-close {\n        duration-ms 150\n        curve \"ease-out-quad\"\n    }\n}\n```\n\n##### `custom-shader`\n\n<sup>Since: 0.1.6</sup>\n\nYou can write a custom shader for drawing the window during a close animation.\n\nSee [this example shader](./examples/close_custom_shader.frag) for a full documentation with several animations to experiment with.\n\nIf a custom shader fails to compile, niri will print a warning and fall back to the default, or previous successfully compiled shader.\nWhen running niri as a systemd service, you can see the warnings in the journal: `journalctl -ef /usr/bin/niri`\n\n> [!WARNING]\n>\n> Custom shaders do not have a backwards compatibility guarantee.\n> I may need to change their interface as I'm developing new features.\n\nExample: close will fill the current geometry with a solid gradient that gradually fades away.\n\n```kdl\nanimations {\n    window-close {\n        custom-shader r\"\n            vec4 close_color(vec3 coords_geo, vec3 size_geo) {\n                vec4 color = vec4(0.0);\n\n                if (0.0 <= coords_geo.x && coords_geo.x <= 1.0\n                        && 0.0 <= coords_geo.y && coords_geo.y <= 1.0)\n                {\n                    vec4 from = vec4(1.0, 0.0, 0.0, 1.0);\n                    vec4 to = vec4(0.0, 1.0, 0.0, 1.0);\n                    color = mix(from, to, coords_geo.y);\n                }\n\n                return color * (1.0 - niri_clamped_progress);\n            }\n        \"\n    }\n}\n```\n\n#### `horizontal-view-movement`\n\nAll horizontal camera view movement animations, such as:\n\n- When a window off-screen is focused and the camera scrolls to it.\n- When a new window appears off-screen and the camera scrolls to it.\n- After a horizontal touchpad gesture (a spring is recommended).\n\n```kdl\nanimations {\n    horizontal-view-movement {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n}\n```\n\n#### `window-movement`\n\n<sup>Since: 0.1.5</sup>\n\nMovement of individual windows within a workspace.\n\nIncludes:\n\n- Moving window columns with `move-column-left` and `move-column-right`.\n- Moving windows inside a column with `move-window-up` and `move-window-down`.\n- Moving windows out of the way upon window opening and closing.\n- Window movement between columns when consuming/expelling.\n\nThis animation *does not* include the camera view movement, such as scrolling the workspace left and right.\n\n```kdl\nanimations {\n    window-movement {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n}\n```\n\n#### `window-resize`\n\n<sup>Since: 0.1.5</sup>\n\nWindow resize animation.\n\nOnly manual window resizes are animated, i.e. when you resize the window with `switch-preset-column-width` or `maximize-column`.\nAlso, very small resizes (up to 10 pixels) are not animated.\n\n```kdl\nanimations {\n    window-resize {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n}\n```\n\n##### `custom-shader`\n\n<sup>Since: 0.1.6</sup>\n\nYou can write a custom shader for drawing the window during a resize animation.\n\nSee [this example shader](./examples/resize_custom_shader.frag) for a full documentation with several animations to experiment with.\n\nIf a custom shader fails to compile, niri will print a warning and fall back to the default, or previous successfully compiled shader.\nWhen running niri as a systemd service, you can see the warnings in the journal: `journalctl -ef /usr/bin/niri`\n\n> [!WARNING]\n>\n> Custom shaders do not have a backwards compatibility guarantee.\n> I may need to change their interface as I'm developing new features.\n\nExample: resize will show the next (after resize) window texture right away, stretched to the current geometry.\n\n```kdl\nanimations {\n    window-resize {\n        custom-shader r\"\n            vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) {\n                vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo;\n                vec4 color = texture2D(niri_tex_next, coords_tex_next.st);\n                return color;\n            }\n        \"\n    }\n}\n```\n\n#### `config-notification-open-close`\n\nThe open/close animation of the config parse error and new default config notifications.\n\nThis one uses an underdamped spring by default (`damping-ratio=0.6`) which causes a slight oscillation in the end.\n\n```kdl\nanimations {\n    config-notification-open-close {\n        spring damping-ratio=0.6 stiffness=1000 epsilon=0.001\n    }\n}\n```\n\n#### `exit-confirmation-open-close`\n\n<sup>Since: 25.08</sup>\n\nThe open/close animation of the exit confirmation dialog.\n\nThis one uses an underdamped spring by default (`damping-ratio=0.6`) which causes a slight oscillation in the end.\n\n```kdl\nanimations {\n    exit-confirmation-open-close {\n        spring damping-ratio=0.6 stiffness=500 epsilon=0.01\n    }\n}\n```\n\n#### `screenshot-ui-open`\n\n<sup>Since: 0.1.8</sup>\n\nThe open (fade-in) animation of the screenshot UI.\n\n```kdl\nanimations {\n    screenshot-ui-open {\n        duration-ms 200\n        curve \"ease-out-quad\"\n    }\n}\n```\n\n#### `overview-open-close`\n\n<sup>Since: 25.05</sup>\n\nThe open/close zoom animation of the [Overview](./Overview.md).\n\n```kdl\nanimations {\n    overview-open-close {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.0001\n    }\n}\n```\n\n#### `recent-windows-close`\n\n<sup>Since: 25.11</sup>\n\nThe close fade-out animation of the recent windows switcher.\n\n```kdl\nanimations {\n    recent-windows-close {\n        spring damping-ratio=1.0 stiffness=800 epsilon=0.001\n    }\n}\n```\n\n### Synchronized Animations\n\n<sup>Since: 0.1.5</sup>\n\nSometimes, when two animations are meant to play together synchronized, niri will drive them both with the same configuration.\n\nFor example, if a window resize causes the view to move, then that view movement animation will also use the `window-resize` configuration (rather than the `horizontal-view-movement` configuration).\nThis is especially important for animated resizes to look good when using `center-focused-column \"always\"`.\n\nAs another example, resizing a window in a column vertically causes other windows to move up or down into their new position.\nThis movement will use the `window-resize` configuration, rather than the `window-movement` configuration, to keep the animations synchronized.\n\nA few actions are still missing this synchronization logic, since in some cases it is difficult to implement properly.\nTherefore, for the best results, consider using the same parameters for related animations (they are all the same by default):\n\n- `horizontal-view-movement`\n- `window-movement`\n- `window-resize`\n"
  },
  {
    "path": "docs/wiki/Configuration:-Debug-Options.md",
    "content": "### Overview\n\nNiri has several options that are only useful for debugging, or are experimental and have known issues.\nThey are not meant for normal use.\n\n> [!CAUTION]\n> These options are **not** covered by the [config breaking change policy](./Configuration:-Introduction.md#breaking-change-policy).\n> They can change or stop working at any point with little notice.\n\nHere are all the options at a glance:\n\n```kdl\ndebug {\n    preview-render \"screencast\"\n    // preview-render \"screen-capture\"\n    enable-overlay-planes\n    disable-cursor-plane\n    disable-direct-scanout\n    restrict-primary-scanout-to-matching-format\n    force-disable-connectors-on-resume\n    render-drm-device \"/dev/dri/renderD129\"\n    ignore-drm-device \"/dev/dri/renderD128\"\n    ignore-drm-device \"/dev/dri/renderD130\"\n    force-pipewire-invalid-modifier\n    dbus-interfaces-in-non-session-instances\n    wait-for-frame-completion-before-queueing\n    emulate-zero-presentation-time\n    disable-resize-throttling\n    disable-transactions\n    keep-laptop-panel-on-when-lid-is-closed\n    disable-monitor-names\n    strict-new-window-focus-policy\n    honor-xdg-activation-with-invalid-serial\n    skip-cursor-only-updates-during-vrr\n    deactivate-unfocused-windows\n}\n\nbinds {\n    Mod+Shift+Ctrl+T { toggle-debug-tint; }\n    Mod+Shift+Ctrl+O { debug-toggle-opaque-regions; }\n    Mod+Shift+Ctrl+D { debug-toggle-damage; }\n}\n```\n\n### `preview-render`\n\nMake niri render the monitors the same way as for a screencast or a screen capture.\n\nUseful for previewing the `block-out-from` window rule.\n\n```kdl\ndebug {\n    preview-render \"screencast\"\n    // preview-render \"screen-capture\"\n}\n```\n\n### `enable-overlay-planes`\n\nEnable direct scanout into overlay planes.\nMay cause frame drops during some animations on some hardware (which is why it is not the default).\n\nDirect scanout into the primary plane is always enabled.\n\n```kdl\ndebug {\n    enable-overlay-planes\n}\n```\n\n### `disable-cursor-plane`\n\nDisable the use of the cursor plane.\nThe cursor will be rendered together with the rest of the frame.\n\nUseful to work around driver bugs on specific hardware.\n\n```kdl\ndebug {\n    disable-cursor-plane\n}\n```\n\n### `disable-direct-scanout`\n\nDisable direct scanout to both the primary plane and the overlay planes.\n\n```kdl\ndebug {\n    disable-direct-scanout\n}\n```\n\n### `restrict-primary-scanout-to-matching-format`\n\nRestricts direct scanout to the primary plane to when the window buffer exactly matches the composition swapchain format.\n\nThis flag may prevent unexpected bandwidth changes when going between composition and scanout.\nThe plan is to make it default in the future, when we implement a way to tell the clients the composition swapchain format.\nAs is, it may prevent some clients (mpv on my machine) from scanning out to the primary plane.\n\n```kdl\ndebug {\n    restrict-primary-scanout-to-matching-format\n}\n```\n\n### `force-disable-connectors-on-resume`\n\nForce-disables all outputs upon resuming niri (TTY switch or waking up from suspend).\nThis causes a modeset/screen blank on all outputs.\n\nIf niri rendering is corrupted, or monitors don't light up after a TTY switch, you can try this flag.\n\n```kdl\ndebug {\n    force-disable-connectors-on-resume\n}\n```\n\n### `render-drm-device`\n\nOverride the DRM device that niri will use for all rendering.\n\nYou can set this to make niri use a different primary GPU than the default one.\n\n```kdl\ndebug {\n    render-drm-device \"/dev/dri/renderD129\"\n}\n```\n\n### `ignore-drm-device`\n\n<sup>Since: 25.11</sup>\n\nList DRM devices that niri will ignore.\nUseful for GPU passthrough when you don't want niri to open a certain device.\n\n```kdl\ndebug {\n    ignore-drm-device \"/dev/dri/renderD128\"\n    ignore-drm-device \"/dev/dri/renderD130\"\n}\n```\n\n### `force-pipewire-invalid-modifier`\n\n<sup>Since: 25.01</sup>\n\nForces PipeWire screencasting to use the invalid modifier, even when DRM offers more modifiers.\n\nUseful for testing the invalid modifier code path that is hit by drivers that don't support modifiers.\n\n```kdl\ndebug {\n    force-pipewire-invalid-modifier\n}\n```\n\n### `dbus-interfaces-in-non-session-instances`\n\nMake niri create its D-Bus interfaces even if it's not running as a `--session`.\n\nUseful for testing screencasting changes without having to relogin.\n\nThe main niri instance will *not* currently take back the interfaces when you close the test instance, so you will need to relogin in the end to make screencasting work again.\n\n```kdl\ndebug {\n    dbus-interfaces-in-non-session-instances\n}\n```\n\n### `wait-for-frame-completion-before-queueing`\n\nWait until every frame is done rendering before handing it over to DRM.\n\nUseful for diagnosing certain synchronization and performance problems.\n\n```kdl\ndebug {\n    wait-for-frame-completion-before-queueing\n}\n```\n\n### `emulate-zero-presentation-time`\n\nEmulate zero (unknown) presentation time returned from DRM.\n\nThis is a thing on NVIDIA proprietary drivers, so this flag can be used to test that niri doesn't break too hard on those systems.\n\n```kdl\ndebug {\n    emulate-zero-presentation-time\n}\n```\n\n### `disable-resize-throttling`\n\n<sup>Since: 0.1.9</sup>\n\nDisable throttling resize events sent to windows.\n\nBy default, when resizing quickly (e.g. interactively), a window will only receive the next size once it has made a commit for the previously requested size.\nThis is required for resize transactions to work properly, and it also helps certain clients which don't batch incoming resizes from the compositor.\n\nDisabling resize throttling will send resizes to windows as fast as possible, which is potentially very fast (for example, on a 1000 Hz mouse).\n\n```kdl\ndebug {\n    disable-resize-throttling\n}\n```\n\n### `disable-transactions`\n\n<sup>Since: 0.1.9</sup>\n\nDisable transactions (resize and close).\n\nBy default, windows which must resize together, do resize together.\nFor example, all windows in a column must resize at the same time to maintain the combined column height equal to the screen height, and to maintain the same window width.\n\nTransactions make niri wait until all windows finish resizing before showing them all on screen in one, synchronized frame.\nFor them to work properly, resize throttling shouldn't be disabled (with the previous debug flag).\n\n```kdl\ndebug {\n    disable-transactions\n}\n```\n\n### `keep-laptop-panel-on-when-lid-is-closed`\n\n<sup>Since: 0.1.10</sup>\n\nBy default, niri will disable the internal laptop monitor when the laptop lid is closed.\nThis flag turns off this behavior and will leave the internal laptop monitor on.\n\n```kdl\ndebug {\n    keep-laptop-panel-on-when-lid-is-closed\n}\n```\n\n### `disable-monitor-names`\n\n<sup>Since: 0.1.10</sup>\n\nDisables the make/model/serial monitor names, as if niri fails to read them from the EDID.\n\nUse this flag to work around a crash present in 0.1.9 and 0.1.10 when connecting two monitors with matching make/model/serial.\n\n```kdl\ndebug {\n    disable-monitor-names\n}\n```\n\n### `strict-new-window-focus-policy`\n\n<sup>Since: 25.01</sup>\n\nDisables heuristic automatic focusing for new windows.\nOnly windows that activate themselves with a valid xdg-activation token will be focused.\n\n```kdl\ndebug {\n    strict-new-window-focus-policy\n}\n```\n\n### `honor-xdg-activation-with-invalid-serial`\n\n<sup>Since: 25.05</sup>\n\nWidely-used clients such as Discord and Telegram make fresh xdg-activation tokens upon clicking on their tray icon or on their notification.\nMost of the time, these fresh tokens will have invalid serials, because the app needs to be focused to get a valid serial, and if the user clicks on a tray icon or a notification, it is usually because the app *isn't* focused, and the user wants to focus it.\n\nBy default, niri ignores xdg-activation tokens with invalid serials, to prevent windows from randomly stealing focus.\nThis debug flag makes niri honor such tokens, making the aforementioned widely-used apps get focus when clicking on their tray icon or notification.\n\nAmusingly, clicking on a notification sends the app a perfectly valid activation token from the notification daemon, but these apps seem to simply ignore it.\nMaybe in the future these apps/toolkits (Electron, Qt) are fixed, making this debug flag unnecessary.\n\n```kdl\ndebug {\n    honor-xdg-activation-with-invalid-serial\n}\n```\n\n### `skip-cursor-only-updates-during-vrr`\n\n<sup>Since: 25.08</sup>\n\nSkips redrawing the screen from cursor input while variable refresh rate is active.\n\nUseful for games where the cursor isn't drawn internally to prevent erratic VRR shifts in response to cursor movement.\n\nNote 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).\n\n```kdl\ndebug {\n    skip-cursor-only-updates-during-vrr\n}\n```\n\n### `deactivate-unfocused-windows`\n\n<sup>Since: 25.08</sup>\n\nSome 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.\nNiri keeps the Activated state on unfocused workspaces and invisible tabbed windows (to reduce unwanted animations), surfacing bugs in these applications.\n\nSet this debug flag to work around these problems.\nIt will cause niri to drop the Activated state for all unfocused windows.\n\n```kdl\ndebug {\n    deactivate-unfocused-windows\n}\n```\n\n### `keep-max-bpc-unchanged`\n\n<sup>Since: 25.08</sup>\n\nWhen 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.\nRestricting 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.\n\nApparently, setting max bpc to 8 breaks some displays driven by AMDGPU.\nIf this happens to you, set this debug flag, which will prevent niri from changing max bpc.\nAMDGPU bug report: https://gitlab.freedesktop.org/drm/amd/-/issues/4487.\n\n<sup>Since: 25.11</sup>\nThis setting is deprecated and does nothing: niri no longer sets max bpc.\nThe old niri behavior with this setting enabled matches the new behavior.\n\n```kdl\ndebug {\n    keep-max-bpc-unchanged\n}\n```\n\n### Key Bindings\n\nThese are not debug options, but rather key bindings.\n\n#### `toggle-debug-tint`\n\nTints all surfaces green, unless they are being directly scanned out.\n\nUseful to check if direct scanout is working.\n\n```kdl\nbinds {\n    Mod+Shift+Ctrl+T { toggle-debug-tint; }\n}\n```\n\n#### `debug-toggle-opaque-regions`\n\n<sup>Since: 0.1.6</sup>\n\nTints regions marked as opaque with blue and the rest of the render elements with red.\n\nUseful to check how Wayland surfaces and internal render elements mark their parts as opaque, which is a rendering performance optimization.\n\n```kdl\nbinds {\n    Mod+Shift+Ctrl+O { debug-toggle-opaque-regions; }\n}\n```\n\n#### `debug-toggle-damage`\n\n<sup>Since: 0.1.6</sup>\n\nTints damaged regions with red.\n\n```kdl\nbinds {\n    Mod+Shift+Ctrl+D { debug-toggle-damage; }\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Gestures.md",
    "content": "### Overview\n\n<sup>Since: 25.02</sup>\n\nThe `gestures` config section contains gesture settings.\nFor an overview of all niri gestures, see the [Gestures](./Gestures.md) wiki page.\n\nHere's a quick glance at the available settings along with their default values.\n\n```kdl\ngestures {\n    dnd-edge-view-scroll {\n        trigger-width 30\n        delay-ms 100\n        max-speed 1500\n    }\n\n    dnd-edge-workspace-switch {\n        trigger-height 50\n        delay-ms 100\n        max-speed 1500\n    }\n\n    hot-corners {\n        // off\n        top-left\n        // top-right\n        // bottom-left\n        // bottom-right\n    }\n}\n```\n\n### `dnd-edge-view-scroll`\n\nScroll the tiling view when moving the mouse cursor against a monitor edge during drag-and-drop (DnD).\nAlso works on a touchscreen.\n\nThis will work for regular drag-and-drop (e.g. dragging a file from a file manager), and for window interactive move when targeting the tiling layout.\n\nThe options are:\n\n- `trigger-width`: size of the area near the monitor edge that will trigger the scrolling, in logical pixels.\n- `delay-ms`: delay in milliseconds before the scrolling starts.\nAvoids unwanted scrolling when dragging things across monitors.\n- `max-speed`: maximum scrolling speed in logical pixels per second.\nThe scrolling speed increases linearly as you move your mouse cursor from `trigger-width` to the very edge of the monitor.\n\n```kdl\ngestures {\n    // Increase the trigger area and maximum speed.\n    dnd-edge-view-scroll {\n        trigger-width 100\n        max-speed 3000\n    }\n}\n```\n\n### `dnd-edge-workspace-switch`\n\n<sup>Since: 25.05</sup>\n\nScroll the workspaces up/down when moving the mouse cursor against a monitor edge during drag-and-drop (DnD) while in the overview.\nAlso works on a touchscreen.\n\nThe options are:\n\n- `trigger-height`: size of the area near the monitor edge that will trigger the scrolling, in logical pixels.\n- `delay-ms`: delay in milliseconds before the scrolling starts.\nAvoids unwanted scrolling when dragging things across monitors.\n- `max-speed`: maximum scrolling speed; 1500 corresponds to one screen height per second.\nThe scrolling speed increases linearly as you move your mouse cursor from `trigger-width` to the very edge of the monitor.\n\n```kdl\ngestures {\n    // Increase the trigger area and maximum speed.\n    dnd-edge-workspace-switch {\n        trigger-height 100\n        max-speed 3000\n    }\n}\n```\n\n### `hot-corners`\n\n<sup>Since: 25.05</sup>\n\nPut your mouse at the very top-left corner of a monitor to toggle the overview.\nAlso works during drag-and-dropping something.\n\n`off` disables the hot corners.\n\n```kdl\n// Disable the hot corners.\ngestures {\n    hot-corners {\n        off\n    }\n}\n```\n\n<sup>Since: 25.11</sup> You can choose specific hot corners by name: `top-left`, `top-right`, `bottom-left`, `bottom-right`.\nIf no corners are explicitly set, the top-left corner will be active by default.\n\n```kdl\n// Enable the top-right and bottom-right hot corners.\ngestures {\n    hot-corners {\n        top-right\n        bottom-right\n    }\n}\n```\n\nYou can also customize hot corners per-output [in the output config](./Configuration:-Outputs.md#hot-corners).\n"
  },
  {
    "path": "docs/wiki/Configuration:-Include.md",
    "content": "<sup>Since: 25.11</sup>\n\nYou can include other files at the top level of the config.\n\n```kdl,must-fail\n// Some settings...\n\ninclude \"colors.kdl\"\n\n// Some more settings...\n```\n\nIncluded files have the same structure as the main config file.\nSettings from included files will be merged with the settings from the main config file.\n\nIncluded config files can in turn include more files.\nAll included files are watched for changes, and the config live-reloads when any of them change.\n\nIncludes work only at the top level of the config:\n\n```kdl,must-fail\n// All good: include at the top level.\ninclude \"something.kdl\"\n\nlayout {\n    // NOT allowed: include inside some other section.\n    include \"other.kdl\"\n}\n```\n\n### Positionality\n\nIncludes are *positional*.\nThey will override options set *prior* to them.\nWindow rules from included files will be inserted at the position of the `include` line.\nFor example:\n\n```kdl\n// colors.kdl\nlayout {\n    border {\n        active-color \"green\"\n    }\n}\n\noverview {\n    backdrop-color \"green\"\n}\n```\n\n```kdl,must-fail\n// config.kdl\nlayout {\n    border {\n        active-color \"red\"\n    }\n}\n\n// This overrides the border color and the backdrop color to green.\ninclude \"colors.kdl\"\n\n// This sets the overview backdrop color to red again.\noverview {\n    backdrop-color \"red\"\n}\n```\n\nThe end result:\n\n- the border color is green (from `colors.kdl`),\n- the overview backdrop color is red (it was set *after* `colors.kdl`).\n\nAnother example:\n\n```kdl\n// rules.kdl\nwindow-rule {\n    match app-id=\"Alacritty\"\n    open-maximized false\n}\n```\n\n```kdl,must-fail\n// config.kdl\nwindow-rule {\n    open-maximized true\n}\n\n// Window rules get inserted at this position.\ninclude \"rules.kdl\"\n\nwindow-rule {\n    match app-id=\"firefox$\"\n    open-maximized true\n}\n```\n\nThis is equivalent to the following config file:\n\n```kdl\nwindow-rule {\n    open-maximized true\n}\n\n// Included from rules.kdl.\nwindow-rule {\n    match app-id=\"Alacritty\"\n    open-maximized false\n}\n\nwindow-rule {\n    match app-id=\"firefox$\"\n    open-maximized true\n}\n```\n\n### Optional includes\n\n<sup>Since: next release</sup>\n\nBy default, including a nonexistent file will cause an error.\nYou can allow nonexistent includes by setting `optional=true`:\n\n```kdl,must-fail\n// Won't fail if this file doesn't exist.\ninclude optional=true \"optional-config.kdl\"\n\n// Regular include, will fail if the file doesn't exist.\ninclude \"required-config.kdl\"\n```\n\nWhen an optional include file is missing, niri will emit a warning in the logs on every config reload.\nThis reminds you that the file is missing while still loading the config successfully.\n\nThe optional file is still watched for changes, so if you create it later, the config will automatically reload and apply the new settings.\n\nNote that `optional` only affects whether a missing file causes an error.\nIf the file exists but contains invalid syntax or other errors, those errors will still cause a parsing failure.\n\n\n### Merging\n\nMost config sections are merged between includes, meaning that you can set only a few properties, and only those properties will change.\n\n```kdl\n// colors.kdl\nlayout {\n    // Does not affect gaps, border width, etc.\n    // Only changes colors as written.\n    focus-ring {\n        active-color \"blue\"\n    }\n\n    border {\n        active-color \"green\"\n    }\n}\n```\n\n```kdl,must-fail\n// config.kdl\ninclude \"colors.kdl\"\n\nlayout {\n    // Does not set border and focus-ring colors,\n    // so colors from colors.kdl are used.\n    gaps 8\n\n    border {\n        width 8\n    }\n}\n```\n\n#### Multipart sections\n\nMultipart sections like `window-rule`, `output`, or `workspace` are inserted as is without merging:\n\n```kdl\n// laptop.kdl\noutput \"eDP-1\" {\n    // ...\n}\n```\n\n```kdl,must-fail\n// config.kdl\noutput \"DP-2\" {\n    // ...\n}\n\ninclude \"laptop.kdl\"\n\n// End result: both DP-2 and eDP-1 settings.\n```\n\n#### Binds\n\n`binds` will override previously-defined conflicting keys:\n\n```kdl\n// binds.kdl\nbinds {\n    Mod+T { spawn \"alacritty\"; }\n}\n```\n\n```kdl,must-fail\n// config.kdl\ninclude \"binds.kdl\"\n\nbinds {\n    // Overrides Mod+T from binds.kdl.\n    Mod+T { spawn \"foot\"; }\n}\n```\n\n#### Flags\n\nMost flags can be disabled with `false`:\n\n```kdl\n// csd.kdl\n\n// Write \"false\" to explicitly disable.\nprefer-no-csd false\n```\n\n```kdl,must-fail\n// config.kdl\n\n// Enable prefer-no-csd in the main config.\nprefer-no-csd\n\n// Including csd.kdl will disable it again.\ninclude \"csd.kdl\"\n```\n\n#### Non-merging sections\n\nSome sections where the contents represent a combined structure are not merged.\nExamples are `struts`, `preset-column-widths`, individual subsections in `animations`, pointing device sections in `input`.\n\n```kdl\n// struts.kdl\nlayout {\n    struts {\n        left 64\n        right 64\n    }\n}\n```\n\n```kdl,must-fail\n// config.kdl\nlayout {\n    struts {\n        top 64\n        bottom 64\n    }\n}\n\ninclude \"struts.kdl\"\n\n// Struts are not merged.\n// End result is only left and right struts.\n```\n\n### Border special case\n\nThere's one special case that differs between the main config and included configs.\n\nWriting `layout { border {} }` in an included config does nothing (since no properties are changed).\nHowever, writing the same in the main config will *enable* the border, i.e. it's equivalent to `layout { border { on; } }`.\n\nSo, if you want to move your layout configuration from the main config to a separate file, remember to add `on` to the border section, for example:\n\n```kdl\n// separate.kdl\nlayout {\n    border {\n        // Add this line:\n        on\n\n        width 4\n        active-color \"#ffc87f\"\n        inactive-color \"#505050\"\n    }\n}\n```\n\nThe reason for this special case is that this is how it historically worked: back when I added borders, we didn't have any `on` flags, so I made writing the `border {}` section enable the border, with an explicit `off` to disable it.\nIt wouldn't be too problematic to change it, however the default config always had a pre-filled `layout { border { off; } }` section with a note saying that commenting out the `off` is enough to enable the border.\nMany people likely have this part of the default config embedded in their configs now, so changing how it works would just cause a lot of confusion.\n"
  },
  {
    "path": "docs/wiki/Configuration:-Input.md",
    "content": "### Overview\n\nIn this section you can configure input devices like keyboard and mouse, and some input-related options.\n\nThere's a section for each device type: `keyboard`, `touchpad`, `mouse`, `trackpoint`, `trackball`, `tablet`, `touch`.\nSettings in those sections will apply to every device of that type.\nCurrently, there's no way to configure specific devices individually (but that is planned).\n\nAll settings at a glance:\n\n```kdl\ninput {\n    keyboard {\n        xkb {\n            // layout \"us\"\n            // variant \"colemak_dh_ortho\"\n            // options \"compose:ralt,ctrl:nocaps\"\n            // model \"\"\n            // rules \"\"\n            // file \"~/.config/keymap.xkb\"\n        }\n\n        // repeat-delay 600\n        // repeat-rate 25\n        // track-layout \"global\"\n        numlock\n    }\n\n    touchpad {\n        // off\n        tap\n        // dwt\n        // dwtp\n        // drag false\n        // drag-lock\n        natural-scroll\n        // accel-speed 0.2\n        // accel-profile \"flat\"\n        // scroll-factor 1.0\n        // scroll-factor vertical=1.0 horizontal=-2.0\n        // scroll-method \"two-finger\"\n        // scroll-button 273\n        // scroll-button-lock\n        // tap-button-map \"left-middle-right\"\n        // click-method \"clickfinger\"\n        // left-handed\n        // disabled-on-external-mouse\n        // middle-emulation\n    }\n\n    mouse {\n        // off\n        // natural-scroll\n        // accel-speed 0.2\n        // accel-profile \"flat\"\n        // scroll-factor 1.0\n        // scroll-factor vertical=1.0 horizontal=-2.0\n        // scroll-method \"no-scroll\"\n        // scroll-button 273\n        // scroll-button-lock\n        // left-handed\n        // middle-emulation\n    }\n\n    trackpoint {\n        // off\n        // natural-scroll\n        // accel-speed 0.2\n        // accel-profile \"flat\"\n        // scroll-method \"on-button-down\"\n        // scroll-button 273\n        // scroll-button-lock\n        // left-handed\n        // middle-emulation\n    }\n\n    trackball {\n        // off\n        // natural-scroll\n        // accel-speed 0.2\n        // accel-profile \"flat\"\n        // scroll-method \"on-button-down\"\n        // scroll-button 273\n        // scroll-button-lock\n        // left-handed\n        // middle-emulation\n    }\n\n    tablet {\n        // off\n        map-to-output \"eDP-1\"\n        // left-handed\n        // calibration-matrix 1.0 0.0 0.0 0.0 1.0 0.0\n    }\n\n    touch {\n        // off\n        map-to-output \"eDP-1\"\n        // calibration-matrix 1.0 0.0 0.0 0.0 1.0 0.0\n    }\n\n    // disable-power-key-handling\n    // warp-mouse-to-focus\n    // focus-follows-mouse max-scroll-amount=\"0%\"\n    // workspace-auto-back-and-forth\n\n    // mod-key \"Super\"\n    // mod-key-nested \"Alt\"\n}\n```\n\n### Keyboard\n\n#### Layout\n\nIn the `xkb` section, you can set layout, variant, options, model and rules.\nThese are passed directly to libxkbcommon, which is also used by most other Wayland compositors.\nSee the `xkeyboard-config(7)` manual for more information.\n\n```kdl\ninput {\n    keyboard {\n        xkb {\n            layout \"us\"\n            variant \"colemak_dh_ortho\"\n            options \"compose:ralt,ctrl:nocaps\"\n        }\n    }\n}\n```\n\n> [!TIP]\n>\n> <sup>Since: 25.02</sup>\n>\n> Alternatively, you can directly set a path to a .xkb file containing an xkb keymap.\n> This overrides all other xkb settings.\n>\n> ```kdl\n> input {\n>     keyboard {\n>         xkb {\n>             file \"~/.config/keymap.xkb\"\n>         }\n>     }\n> }\n> ```\n\n> [!NOTE]\n>\n> <sup>Since: 25.08</sup>\n>\n> 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.\n> This way, for example, system installers can dynamically set the niri keyboard layout.\n> You can see this layout in `localectl` and change it with `localectl set-x11-keymap`, for example:\n>\n> ```sh\n> $ localectl set-x11-keymap \"us\" \"\" \"colemak_dh_ortho\" \"compose:ralt,ctrl:nocaps\"\n> $ localectl\n> System Locale: LANG=en_US.UTF-8\n>                LC_NUMERIC=ru_RU.UTF-8\n>                LC_TIME=ru_RU.UTF-8\n>                LC_MONETARY=ru_RU.UTF-8\n>                LC_PAPER=ru_RU.UTF-8\n>                LC_MEASUREMENT=ru_RU.UTF-8\n>     VC Keymap: us-colemak_dh_ortho\n>    X11 Layout: us\n>   X11 Variant: colemak_dh_ortho\n>   X11 Options: compose:ralt,ctrl:nocaps\n> ```\n>\n> By default, `localectl` will set the TTY keymap to the closest match of the XKB keymap.\n> You can prevent that with a `--no-convert` flag, for example: `localectl set-x11-keymap --no-convert \"us,ru\"`.\n>\n> These settings are picked up by some other programs too, like GDM.\n\nWhen using multiple layouts, niri can remember the current layout globally (the default) or per-window.\nYou can control this with the `track-layout` option.\n\n- `global`: layout change is global for all windows.\n- `window`: layout is tracked for each window individually.\n\n```kdl\ninput {\n    keyboard {\n        track-layout \"global\"\n    }\n}\n```\n\n#### Repeat\n\nDelay is in milliseconds before the keyboard repeat starts.\nRate is in characters per second.\n\n```kdl\ninput {\n    keyboard {\n        repeat-delay 600\n        repeat-rate 25\n    }\n}\n```\n\n#### Num Lock\n\n<sup>Since: 25.05</sup>\n\nSet the `numlock` flag to turn on Num Lock automatically at startup.\n\nYou might want to disable (comment out) `numlock` if you're using a laptop with a keyboard that overlays Num Lock keys on top of regular keys.\n\n```kdl\ninput {\n    keyboard {\n        numlock\n    }\n}\n```\n\n### Pointing Devices\n\nMost settings for the pointing devices are passed directly to libinput.\nOther Wayland compositors also use libinput, so it's likely you will find the same settings there.\nFor flags like `tap`, omit them or comment them out to disable the setting.\n\nA few settings are common between input devices:\n\n- `off`: if set, no events will be sent from this device.\n\nA few settings are common between `touchpad`, `mouse`, `trackpoint`, and `trackball`:\n\n- `natural-scroll`: if set, inverts the scrolling direction.\n- `accel-speed`: pointer acceleration speed, valid values are from `-1.0` to `1.0` where the default is `0.0`.\n- `accel-profile`: can be `adaptive` (the default) or `flat` (disables pointer acceleration).\n- `scroll-method`: when to generate scroll events instead of pointer motion events, can be `no-scroll`, `two-finger`, `edge`, or `on-button-down`.\n  The default and supported methods vary depending on the device type.\n- `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`.\n- `scroll-button-lock`: <sup>Since: 25.08</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.\n- `left-handed`: if set, changes the device to left-handed mode.\n- `middle-emulation`: emulate a middle mouse click by pressing left and right mouse buttons at once.\n\nSettings specific to `touchpad`s:\n\n- `tap`: tap-to-click.\n- `dwt`: disable-when-typing.\n- `dwtp`: disable-when-trackpointing.\n- `drag`: <sup>Since: 25.05</sup> can be `true` or `false`, controls if tap-and-drag is enabled.\n- `drag-lock`: <sup>Since: 25.02</sup> if set, lifting the finger off for a short time while dragging will not drop the dragged item. See the [libinput documentation](https://wayland.freedesktop.org/libinput/doc/latest/tapping.html#tap-and-drag).\n- `tap-button-map`: can be `left-right-middle` or `left-middle-right`, controls which button corresponds to a two-finger tap and a three-finger tap.\n- `click-method`: can be `button-areas` or `clickfinger`, changes the [click method](https://wayland.freedesktop.org/libinput/doc/latest/clickpad-softbuttons.html).\n- `disabled-on-external-mouse`: do not send events while external pointer device is plugged in.\n\nSettings specific to `touchpad` and `mouse`:\n\n- `scroll-factor`: <sup>Since: 0.1.10</sup> scales the scrolling speed by this value.\n\n    <sup>Since: 25.08</sup> You can also override horizontal and vertical scroll factor separately like so: `scroll-factor horizontal=2.0 vertical=-1.0`\n\nSettings specific to `tablet` and `touch`:\n\n- `calibration-matrix`: 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.\n    - <sup>Since: 25.02</sup> for `tablet`\n    - <sup>Since: 25.11</sup> for `touch`\n\nTablets and touchscreens are absolute pointing devices that can be mapped to a specific output like so:\n\n```kdl\ninput {\n    tablet {\n        map-to-output \"eDP-1\"\n    }\n\n    touch {\n        map-to-output \"eDP-1\"\n    }\n}\n```\n\nValid output names are the same as the ones used for output configuration.\n\n<sup>Since: 0.1.7</sup> When a tablet is not mapped to any output, it will map to the union of all connected outputs, without aspect ratio correction.\n\n### General Settings\n\nThese settings are not specific to a particular input device.\n\n#### `disable-power-key-handling`\n\nBy default, niri will take over the power button to make it sleep instead of power off.\nSet this if you would like to configure the power button elsewhere (i.e. `logind.conf`).\n\n```kdl\ninput {\n    disable-power-key-handling\n}\n```\n\n#### `warp-mouse-to-focus`\n\nMakes the mouse warp to newly focused windows.\n\nDoes not make the cursor visible if it had been hidden.\n\n```kdl\ninput {\n    warp-mouse-to-focus\n}\n```\n\nBy default, the cursor warps *separately* horizontally and vertically.\nI.e. if moving the mouse only horizontally is enough to put it inside the newly focused window, then the mouse will move only horizontally, and not vertically.\n\n<sup>Since: 25.05</sup> You can customize this with the `mode` property.\n\n- `mode=\"center-xy\"`: warps by both X and Y coordinates together.\nSo if the mouse was anywhere outside the newly focused window, it will warp to the center of the window.\n- `mode=\"center-xy-always\"`: warps by both X and Y coordinates together, even if the mouse was already somewhere inside the newly focused window.\n\n```kdl\ninput {\n    warp-mouse-to-focus mode=\"center-xy\"\n}\n```\n\n#### `focus-follows-mouse`\n\nFocuses windows and outputs automatically when moving the mouse over them.\n\n```kdl\ninput {\n    focus-follows-mouse\n}\n```\n\n<sup>Since: 0.1.8</sup> You can optionally set `max-scroll-amount`.\nThen, focus-follows-mouse won't focus a window if it will result in the view scrolling more than the set amount.\nThe value is a percentage of the working area width.\n\n```kdl\ninput {\n    // Allow focus-follows-mouse when it results in scrolling at most 10% of the screen.\n    focus-follows-mouse max-scroll-amount=\"10%\"\n}\n```\n\n```kdl\ninput {\n    // Allow focus-follows-mouse only when it will not scroll the view.\n    focus-follows-mouse max-scroll-amount=\"0%\"\n}\n```\n\n#### `workspace-auto-back-and-forth`\n\nNormally, switching to the same workspace by index twice will do nothing (since you're already on that workspace).\nIf this flag is enabled, switching to the same workspace by index twice will switch back to the previous workspace.\n\nNiri will correctly switch to the workspace you came from, even if workspaces were reordered in the meantime.\n\n```kdl\ninput {\n    workspace-auto-back-and-forth\n}\n```\n\n#### `mod-key`, `mod-key-nested`\n\n<sup>Since: 25.05</sup>\n\nCustomize the `Mod` key for [key bindings](./Configuration:-Key-Bindings.md).\nOnly valid modifiers are allowed, e.g. `Super`, `Alt`, `Mod3`, `Mod5`, `Ctrl`, `Shift`.\n\nBy default, `Mod` is equal to `Super` when running niri on a TTY, and to `Alt` when running niri as a nested winit window.\n\n> [!NOTE]\n> There are a lot of default bindings with Mod, none of them \"make it through\" to the underlying window.\n> You probably don't want to set `mod-key` to Ctrl or Shift, since Ctrl is commonly used for app hotkeys, and Shift is used for, well, regular typing.\n\n```kdl\n// Switch the mod keys around: use Alt normally, and Super inside a nested window.\ninput {\n    mod-key \"Alt\"\n    mod-key-nested \"Super\"\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Introduction.md",
    "content": "### Per-Section Documentation\n\nYou can find documentation for various sections of the config on these wiki pages:\n\n* [`input {}`](./Configuration:-Input.md)\n* [`output \"eDP-1\" {}`](./Configuration:-Outputs.md)\n* [`binds {}`](./Configuration:-Key-Bindings.md)\n* [`switch-events {}`](./Configuration:-Switch-Events.md)\n* [`layout {}`](./Configuration:-Layout.md)\n* [top-level options](./Configuration:-Miscellaneous.md)\n* [`window-rule {}`](./Configuration:-Window-Rules.md)\n* [`layer-rule {}`](./Configuration:-Layer-Rules.md)\n* [`animations {}`](./Configuration:-Animations.md)\n* [`gestures {}`](./Configuration:-Gestures.md)\n* [`recent-windows {}`](./Configuration:-Recent-Windows.md)\n* [`debug {}`](./Configuration:-Debug-Options.md)\n* [`include \"other.kdl\"`](./Configuration:-Include.md)\n\n### Loading\n\nNiri will load configuration from `$XDG_CONFIG_HOME/niri/config.kdl` or `~/.config/niri/config.kdl`, falling back to `/etc/niri/config.kdl`.\nIf both of these files are missing, niri will create `$XDG_CONFIG_HOME/niri/config.kdl` with the contents of [the default configuration file](https://github.com/niri-wm/niri/blob/main/resources/default-config.kdl), which are embedded into the niri binary at build time.\nPlease use the default configuration file as the starting point for your custom configuration.\n\nThe configuration is live-reloaded.\nSimply edit and save the config file, and your changes will be applied.\nThis includes key bindings, output settings like mode, window rules, and everything else.\n\nYou can run `niri validate` to parse the config and see any errors.\n\nTo use a different config file path, pass it in the `--config` or `-c` argument to `niri`.\n\nYou can also set `$NIRI_CONFIG` to the path of the config file.\n`--config` always takes precedence.\nIf `--config` or `$NIRI_CONFIG` doesn't point to a real file, the config will not be loaded.\nIf `$NIRI_CONFIG` is set to an empty string, it is ignored and the default config location is used instead.\n\n### Syntax\n\nThe config is written in [KDL].\n\n#### Comments\n\nLines starting with `//` are comments; they are ignored.\n\nAlso, you can put `/-` in front of a section to comment out the entire section:\n\n```kdl\n/-output \"eDP-1\" {\n    // Everything inside here is ignored.\n    // The display won't be turned off\n    // as the whole section is commented out.\n    off\n}\n```\n\n#### Flags\n\nToggle options in niri are commonly represented as flags.\nWriting out the flag enables it, and omitting it or commenting it out disables it.\nFor example:\n\n```kdl\n// \"Focus follows mouse\" is enabled.\ninput {\n    focus-follows-mouse\n\n    // Other settings...\n}\n```\n\n```kdl\n// \"Focus follows mouse\" is disabled.\ninput {\n    // focus-follows-mouse\n\n    // Other settings...\n}\n```\n\n#### Sections\n\nMost sections cannot be repeated. For example:\n\n```kdl\n// This is valid: every section appears once.\ninput {\n    keyboard {\n        // ...\n    }\n\n    touchpad {\n        // ...\n    }\n}\n```\n\n```kdl,must-fail\n// This is NOT valid: input section appears twice.\ninput {\n    keyboard {\n        // ...\n    }\n}\n\ninput {\n    touchpad {\n        // ...\n    }\n}\n```\n\nExceptions are, for example, sections that configure different devices by name:\n\n<!-- NOTE: this may break in the future -->\n```kdl\noutput \"eDP-1\" {\n    // ...\n}\n\n// This is valid: this section configures a different output.\noutput \"HDMI-A-1\" {\n    // ...\n}\n\n// This is NOT valid: \"eDP-1\" already appeared above.\n// It will either throw a config parsing error, or otherwise not work.\noutput \"eDP-1\" {\n    // ...\n}\n```\n\n### Defaults\n\nOmitting most of the sections of the config file will leave you with the default values for that section.\nA notable exception is [`binds {}`](./Configuration:-Key-Bindings.md): they do not get filled with defaults, so make sure you do not erase this section.\n\n### Breaking Change Policy\n\nAs a rule, niri updates should not break existing config files.\n(For example, the default config from niri v0.1.0 still parses fine on v25.02 as I'm writing this.)\n\nExceptions can be made for parsing bugs.\nFor example, niri used to accept multiple binds to the same key, but this was not intended and did not do anything (the first bind was always used).\nA patch release changed niri from silently accepting this to causing a parsing failure.\nThis is not a blanket rule, I will consider the potential impact of every breaking change like this before deciding to carry on with it.\n\nKeep in mind that the breaking change policy applies only to niri releases.\nCommits between releases can and do occasionally break the config as new features are ironed out.\nHowever, I do try to limit these, since several people are running git builds.\n\n[KDL]: https://kdl.dev/\n"
  },
  {
    "path": "docs/wiki/Configuration:-Key-Bindings.md",
    "content": "### Overview\n\nKey bindings are declared in the `binds {}` section of the config.\n\n> [!NOTE]\n> This is one of the few sections that *does not* get automatically filled with defaults if you omit it, so make sure to copy it from the default config.\n\nEach bind is a hotkey followed by one action enclosed in curly brackets.\nFor example:\n\n```kdl\nbinds {\n    Mod+Left { focus-column-left; }\n    Super+Alt+L { spawn \"swaylock\"; }\n}\n```\n\nThe hotkey consists of modifiers separated by `+` signs, followed by an XKB key name in the end.\n\nValid modifiers are:\n\n- `Ctrl` or `Control`;\n- `Shift`;\n- `Alt`;\n- `Super` or `Win`;\n- `ISO_Level3_Shift` or `Mod5`—this is the AltGr key on certain layouts;\n- `ISO_Level5_Shift`: can be used with an xkb lv5 option like `lv5:caps_switch`;\n- `Mod`.\n\n`Mod` is a special modifier that is equal to `Super` when running niri on a TTY, and to `Alt` when running niri as a nested winit window.\nThis way, you can test niri in a window without causing too many conflicts with the host compositor's key bindings.\nFor this reason, most of the default keys use the `Mod` modifier.\n\n<sup>Since: 25.05</sup> You can customize the `Mod` key [in the `input` section of the config](./Configuration:-Input.md#mod-key-mod-key-nested).\n\n> [!TIP]\n> To find an XKB name for a particular key, you may use a program like [`wev`](https://git.sr.ht/~sircmpwn/wev).\n>\n> Open it from a terminal and press the key that you want to detect.\n> In the terminal, you will see output like this:\n>\n> ```\n> [14:     wl_keyboard] key: serial: 757775; time: 44940343; key: 113; state: 1 (pressed)\n>                       sym: Left         (65361), utf8: ''\n> [14:     wl_keyboard] key: serial: 757776; time: 44940432; key: 113; state: 0 (released)\n>                       sym: Left         (65361), utf8: ''\n> [14:     wl_keyboard] key: serial: 757777; time: 44940753; key: 114; state: 1 (pressed)\n>                       sym: Right        (65363), utf8: ''\n> [14:     wl_keyboard] key: serial: 757778; time: 44940846; key: 114; state: 0 (released)\n>                       sym: Right        (65363), utf8: ''\n> ```\n>\n> Here, look at `sym: Left` and `sym: Right`: these are the key names.\n> I was pressing the left and the right arrow in this example.\n>\n> Keep in mind that binding shifted keys requires spelling out Shift and the unshifted version of the key, according to your XKB layout.\n> For example, on the US QWERTY layout, <kbd>&lt;</kbd> is on <kbd>Shift</kbd> + <kbd>,</kbd>, so to bind it, you spell out something like `Mod+Shift+Comma`.\n>\n> As another example, if you've configured the French [BÉPO](https://en.wikipedia.org/wiki/B%C3%89PO) XKB layout, your <kbd>&lt;</kbd> is on <kbd>AltGr</kbd> + <kbd>«</kbd>.\n> <kbd>AltGr</kbd> is `ISO_Level3_Shift`, or equivalently `Mod5`, so to bind it, you spell out something like `Mod+Mod5+guillemotleft`.\n>\n> When resolving latin keys, niri will search for the *first* configured XKB layout that has the latin key.\n> So for example with US QWERTY and RU layouts configured, US QWERTY will be used for latin binds.\n\n<sup>Since: 0.1.8</sup> Binds will repeat by default (i.e. holding down a bind will make it trigger repeatedly).\nYou can disable that for specific binds with `repeat=false`:\n\n```kdl\nbinds {\n    Mod+T repeat=false { spawn \"alacritty\"; }\n}\n```\n\nBinds can also have a cooldown, which will rate-limit the bind and prevent it from repeatedly triggering too quickly.\n\n```kdl\nbinds {\n    Mod+T cooldown-ms=500 { spawn \"alacritty\"; }\n}\n```\n\nThis is mostly useful for the scroll bindings.\n\n### Scroll Bindings\n\nYou can bind mouse wheel scroll ticks using the following syntax.\nThese binds will change direction based on the `natural-scroll` setting.\n\n```kdl\nbinds {\n    Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }\n    Mod+WheelScrollUp   cooldown-ms=150 { focus-workspace-up; }\n    Mod+WheelScrollRight                { focus-column-right; }\n    Mod+WheelScrollLeft                 { focus-column-left; }\n}\n```\n\nSimilarly, you can bind touchpad scroll \"ticks\".\nTouchpad scrolling is continuous, so for these binds it is split into discrete intervals based on distance travelled.\n\nThese binds are also affected by touchpad's `natural-scroll`, so these example binds are \"inverted\", since niri has `natural-scroll` enabled for touchpads by default.\n\n```kdl\nbinds {\n    Mod+TouchpadScrollDown { spawn \"wpctl\" \"set-volume\" \"@DEFAULT_AUDIO_SINK@\" \"0.02+\"; }\n    Mod+TouchpadScrollUp   { spawn \"wpctl\" \"set-volume\" \"@DEFAULT_AUDIO_SINK@\" \"0.02-\"; }\n}\n```\n\nBoth mouse wheel and touchpad scroll binds will prevent applications from receiving any scroll events when their modifiers are held down.\nFor example, if you have a `Mod+WheelScrollDown` bind, then while holding `Mod`, all mouse wheel scrolling will be consumed by niri.\n\n### Mouse Click Bindings\n\n<sup>Since: 25.01</sup>\n\nYou can bind mouse clicks using the following syntax.\n\n```kdl\nbinds {\n    Mod+MouseLeft    { close-window; }\n    Mod+MouseRight   { close-window; }\n    Mod+MouseMiddle  { close-window; }\n    Mod+MouseForward { close-window; }\n    Mod+MouseBack    { close-window; }\n}\n```\n\nMouse clicks operate on the window that was focused at the time of the click, not the window you're clicking.\n\nNote that binding `Mod+MouseLeft` or `Mod+MouseRight` will override the corresponding gesture (moving or resizing the window).\n\n### Custom Hotkey Overlay Titles\n\n<sup>Since: 25.02</sup>\n\nThe hotkey overlay (the Important Hotkeys dialog) shows a hardcoded list of binds.\nYou can customize this list using the `hotkey-overlay-title` property.\n\nTo add a bind to the hotkey overlay, set the property to the title that you want to show:\n```kdl\nbinds {\n    Mod+Shift+S hotkey-overlay-title=\"Toggle Dark/Light Style\" { spawn \"some-script.sh\"; }\n}\n```\n\nBinds with custom titles are listed after the hardcoded binds and before non-customized Spawn binds.\n\nTo remove a hardcoded bind from the hotkey overlay, set the property to null:\n```kdl\nbinds {\n    Mod+Q hotkey-overlay-title=null { close-window; }\n}\n```\n\n> [!TIP]\n> When multiple key combinations are bound to the same action:\n> - If any of the binds has a custom hotkey overlay title, niri will show that bind.\n> - Otherwise, if any of the binds has a null title, niri will hide the bind.\n> - Otherwise, niri will show the first key combination.\n\nCustom titles support [Pango markup](https://docs.gtk.org/Pango/pango_markup.html):\n\n```kdl\nbinds {\n    Mod+Shift+S hotkey-overlay-title=\"<b>Toggle</b> <span foreground='red'>Dark</span>/Light Style\" { spawn \"some-script.sh\"; }\n}\n```\n\n![Custom markup example.](https://github.com/user-attachments/assets/2a2ba914-bfa7-4dfa-bb5e-49839034765d)\n\n### Actions\n\nEvery action that you can bind is also available for programmatic invocation via `niri msg action`.\nRun `niri msg action` to get a full list of actions along with their short descriptions.\n\nHere are a few actions that benefit from more explanation.\n\n#### `spawn`\n\nRun a program.\n\n`spawn` accepts a path to the program binary as the first argument, followed by arguments to the program.\nFor example:\n\n```kdl\nbinds {\n    // Run alacritty.\n    Mod+T { spawn \"alacritty\"; }\n\n    // Run `wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+`.\n    XF86AudioRaiseVolume { spawn \"wpctl\" \"set-volume\" \"@DEFAULT_AUDIO_SINK@\" \"0.1+\"; }\n}\n```\n\n> [!TIP]\n>\n> <sup>Since: 0.1.5</sup>\n>\n> Spawn bindings have a special `allow-when-locked=true` property that makes them work even while the session is locked:\n>\n> ```kdl\n> binds {\n>     // This mute bind will work even when the session is locked.\n>     XF86AudioMute allow-when-locked=true { spawn \"wpctl\" \"set-mute\" \"@DEFAULT_AUDIO_SINK@\" \"toggle\"; }\n> }\n> ```\n\nFor `spawn`, niri *does not* use a shell to run commands, which means that you need to manually separate arguments.\nSee [`spawn-sh`](#spawn-sh) below for an action that uses a shell.\n\n```kdl\nbinds {\n    // Correct: every argument is in its own quotes.\n    Mod+T { spawn \"alacritty\" \"-e\" \"/usr/bin/fish\"; }\n\n    // Wrong: will interpret the whole `alacritty -e /usr/bin/fish` string as the binary path.\n    Mod+D { spawn \"alacritty -e /usr/bin/fish\"; }\n\n    // Wrong: will pass `-e /usr/bin/fish` as one argument, which alacritty won't understand.\n    Mod+Q { spawn \"alacritty\" \"-e /usr/bin/fish\"; }\n}\n```\n\nThis also means that you cannot expand environment variables or `~`.\nIf you need this, you can run the command through a shell manually.\n\n```kdl\nbinds {\n    // Wrong: no shell expansion here. These strings will be passed literally to the program.\n    Mod+T { spawn \"grim\" \"-o\" \"$MAIN_OUTPUT\" \"~/screenshot.png\"; }\n\n    // Correct: run this through a shell manually so that it can expand the arguments.\n    // Note that the entire command is passed as a SINGLE argument,\n    // because shell will do its own argument splitting by whitespace.\n    Mod+D { spawn \"sh\" \"-c\" \"grim -o $MAIN_OUTPUT ~/screenshot.png\"; }\n\n    // You can also use a shell to run multiple commands,\n    // use pipes, process substitution, and so on.\n    Mod+Q { spawn \"sh\" \"-c\" \"notify-send clipboard \\\"$(wl-paste)\\\"\"; }\n}\n```\n\nAs a special case, niri will expand `~` to the home directory *only* at the beginning of the program name.\n\n```kdl\nbinds {\n    // This will work: one ~ at the very beginning.\n    Mod+T { spawn \"~/scripts/do-something.sh\"; }\n}\n```\n\n#### `spawn-sh`\n\n<sup>Since: 25.08</sup>\n\nRun a command through the shell.\n\nThe argument is a single string that is passed verbatim to `sh`.\nYou can use shell variables, pipelines, `~` expansion, and everything else as expected.\n\n```kdl\nbinds {\n    // Works with spawn-sh: all arguments in the same string.\n    Mod+D { spawn-sh \"alacritty -e /usr/bin/fish\"; }\n\n    // Works with spawn-sh: shell variable ($MAIN_OUTPUT), ~ expansion.\n    Mod+T { spawn-sh \"grim -o $MAIN_OUTPUT ~/screenshot.png\"; }\n\n    // Works with spawn-sh: process substitution.\n    Mod+Q { spawn-sh \"notify-send clipboard \\\"$(wl-paste)\\\"\"; }\n\n    // Works with spawn-sh: multiple commands.\n    Super+Alt+S { spawn-sh \"pkill orca || exec orca\"; }\n}\n```\n\n`spawn-sh \"some command\"` is equivalent to `spawn \"sh\" \"-c\" \"some command\"`—it's just a less confusing shorthand.\nKeep in mind that going through the shell incurs a tiny performance penalty compared to directly `spawn`ing some binary.\n\nUsing `sh` is hardcoded, consistent with other compositors.\nIf you want a different shell, write it out using `spawn`, e.g. `spawn \"fish\" \"-c\" \"some fish command\"`.\n\n#### `quit`\n\nExit niri after showing a confirmation dialog to avoid accidentally triggering it.\n\n```kdl\nbinds {\n    Mod+Shift+E { quit; }\n}\n```\n\nIf you want to skip the confirmation dialog, set the flag like so:\n\n```kdl\nbinds {\n    Mod+Shift+E { quit skip-confirmation=true; }\n}\n```\n\n#### `do-screen-transition`\n\n<sup>Since: 0.1.6</sup>\n\nFreeze the screen for a brief moment then crossfade to the new contents.\n\n```kdl\nbinds {\n    Mod+Return { do-screen-transition; }\n}\n```\n\nThis action is mainly useful to trigger from scripts changing the system theme or style (between light and dark for example).\nIt makes transitions like this, where windows change their style one by one, look smooth and synchronized.\n\nFor example, using the GNOME color scheme setting:\n\n```shell\nniri msg action do-screen-transition\ndconf write /org/gnome/desktop/interface/color-scheme \"\\\"prefer-dark\\\"\"\n```\n\nBy default, the screen is frozen for 250 ms to give windows time to redraw, before the crossfade.\nYou can set this delay like this:\n\n```kdl\nbinds {\n    Mod+Return { do-screen-transition delay-ms=100; }\n}\n```\n\nOr, in scripts:\n\n```shell\nniri msg action do-screen-transition --delay-ms 100\n```\n\n#### `toggle-window-rule-opacity`\n\n<sup>Since: 25.02</sup>\n\nToggle the opacity window rule of the focused window.\nThis only has an effect if the window's opacity window rule is already set to semitransparent.\n\n```kdl\nbinds {\n    Mod+O { toggle-window-rule-opacity; }\n}\n```\n\n#### `screenshot`, `screenshot-screen`, `screenshot-window`\n\nActions for taking screenshots.\n\n- `screenshot`: opens the built-in interactive screenshot UI.\n- `screenshot-screen`, `screenshot-window`: takes a screenshot of the focused screen or window respectively.\n\nThe screenshot is both stored to the clipboard and saved to disk, according to the [`screenshot-path` option](./Configuration:-Miscellaneous.md#screenshot-path).\n\n<sup>Since: 25.02</sup> You can disable saving to disk for a specific bind with the `write-to-disk=false` property:\n\n```kdl\nbinds {\n    Ctrl+Print { screenshot-screen write-to-disk=false; }\n    Alt+Print { screenshot-window write-to-disk=false; }\n}\n```\n\nIn the interactive screenshot UI, pressing <kbd>Ctrl</kbd><kbd>C</kbd> will copy the screenshot to the clipboard without writing it to disk.\n\n<sup>Since: 25.05</sup> You can hide the mouse pointer in screenshots with the `show-pointer=false` property:\n\n```kdl\nbinds {\n    // The pointer will be hidden by default\n    // (you can still show it by pressing P).\n    Print { screenshot show-pointer=false; }\n\n    // The pointer will be hidden on the screenshot.\n    Ctrl+Print { screenshot-screen show-pointer=false; }\n}\n```\n\n<sup>Since: next release</sup> You can show the mouse pointer on window screenshots with the `show-pointer=true` property.\nThe pointer will be included only if the window is currently receiving pointer input (usually this means the pointer is on top of the window).\n\n```kdl\nbinds {\n    // The pointer will be visible on the screenshot\n    // if it's on top of the window.\n    Alt+Print { screenshot-window show-pointer=true; }\n}\n```\n\n#### `toggle-keyboard-shortcuts-inhibit`\n\n<sup>Since: 25.02</sup>\n\nApplications such as remote-desktop clients and software KVM switches may request that niri stops processing its keyboard shortcuts so that they may, for example, forward the key presses as-is to a remote machine.\n`toggle-keyboard-shortcuts-inhibit` is an escape hatch that toggles the inhibitor.\nIt's a good idea to bind it, so a buggy application can't hold your session hostage.\n\n```kdl\nbinds {\n    Mod+Escape { toggle-keyboard-shortcuts-inhibit; }\n}\n```\n\nYou can also make certain binds ignore inhibiting with the `allow-inhibiting=false` property.\nThey will always be handled by niri and never passed to the window.\n\n```kdl\nbinds {\n    // This bind will always work, even when using a virtual machine.\n    Super+Alt+L allow-inhibiting=false { spawn \"swaylock\"; }\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Layer-Rules.md",
    "content": "### Overview\n\n<sup>Since: 25.01</sup>\n\nLayer rules let you adjust behavior for individual layer-shell surfaces.\nThey have `match` and `exclude` directives that control which layer-shell surfaces the rule should apply to, and a number of properties that you can set.\n\nLayer rules are processed and work very similarly to window rules, just with different matchers and properties.\nPlease read the [window rules wiki page](./Configuration:-Window-Rules.md) to learn how matching works.\n\nHere are all matchers and properties that a layer rule could have:\n\n```kdl\nlayer-rule {\n    match namespace=\"waybar\"\n    match at-startup=true\n\n    // Properties that apply continuously.\n    opacity 0.5\n    block-out-from \"screencast\"\n    // block-out-from \"screen-capture\"\n\n    shadow {\n        on\n        // off\n        softness 40\n        spread 5\n        offset x=0 y=5\n        draw-behind-window true\n        color \"#00000064\"\n        // inactive-color \"#00000064\"\n    }\n\n    geometry-corner-radius 12\n    place-within-backdrop true\n    baba-is-float true\n}\n```\n\n### Layer Surface Matching\n\nLet's look at the matchers in more detail.\n\n#### `namespace`\n\nThis is a regular expression that should match anywhere in the surface namespace.\nYou can read about the supported regular expression syntax [here](https://docs.rs/regex/latest/regex/#syntax).\n\n```kdl\n// Match surfaces with namespace containing \"waybar\",\nlayer-rule {\n    match namespace=\"waybar\"\n}\n```\n\nYou can find the namespaces of all open layer-shell surfaces by running `niri msg layers`.\n\n#### `at-startup`\n\nCan be `true` or `false`.\nMatches during the first 60 seconds after starting niri.\n\n```kdl\n// Show layer-shell surfaces with 0.5 opacity at niri startup, but not afterwards.\nlayer-rule {\n    match at-startup=true\n\n    opacity 0.5\n}\n```\n\n### Dynamic Properties\n\nThese properties apply continuously to open layer-shell surfaces.\n\n#### `block-out-from`\n\nYou can block out surfaces from xdg-desktop-portal screencasts or all screen captures.\nThey will be replaced with solid black rectangles.\n\nThis can be useful for notifications.\n\nThe same caveats and instructions apply as for the [`block-out-from` window rule](./Configuration:-Window-Rules.md#block-out-from), so check the documentation there.\n\n![Screenshot showing a notification visible normally, but blocked out on OBS.](./img/layer-block-out-from-screencast.png)\n\n```kdl\n// Block out mako notifications from screencasts.\nlayer-rule {\n    match namespace=\"^notifications$\"\n\n    block-out-from \"screencast\"\n}\n```\n\n#### `opacity`\n\nSet the opacity of the surface.\n`0.0` is fully transparent, `1.0` is fully opaque.\nThis is applied on top of the surface's own opacity, so semitransparent surfaces will become even more transparent.\n\nOpacity is applied to every child of the layer-shell surface individually, so subsurfaces and pop-up menus will show window content behind them.\n\n```kdl\n// Make fuzzel semitransparent.\nlayer-rule {\n    match namespace=\"^launcher$\"\n\n    opacity 0.95\n}\n```\n\n#### `shadow`\n\n<sup>Since: 25.02</sup>\n\nOverride the shadow options for the surface.\n\nThese rules have the same options as the normal [`shadow` config in the layout section](./Configuration:-Layout.md#shadow), so check the documentation there.\n\nUnlike window shadows, layer surface shadows always need to be enabled with a layer rule.\nThat is, enabling shadows in the layout config section won't automatically enable them for layer surfaces.\n\n> [!NOTE]\n> Layer surfaces have no way to tell niri about their *visual geometry*.\n> For example, if a layer surface includes some invisible margins (like mako), niri has no way of knowing that, and will draw the shadow behind the entire surface, including the invisible margins.\n>\n> So to use niri shadows, you'll need to configure layer-shell clients to remove their own margins or shadows.\n\n```kdl\n// Add a shadow for fuzzel.\nlayer-rule {\n    match namespace=\"^launcher$\"\n\n    shadow {\n        on\n    }\n\n    // Fuzzel defaults to 10 px rounded corners.\n    geometry-corner-radius 10\n}\n```\n\n#### `geometry-corner-radius`\n\n<sup>Since: 25.02</sup>\n\nSet the corner radius of the surface.\n\nThis setting will only affect the shadow—it will round its corners to match the geometry corner radius.\n\n```kdl\nlayer-rule {\n    match namespace=\"^launcher$\"\n\n    geometry-corner-radius 12\n}\n```\n\n#### `place-within-backdrop`\n\n<sup>Since: 25.05</sup>\n\nSet to `true` to place the surface into the backdrop visible in the [Overview](./Overview.md) and between workspaces.\n\nThis will only work for *background* layer surfaces that ignore exclusive zones (typical for wallpaper tools).\nLayers within the backdrop will ignore all input.\n\n```kdl\n// Put swaybg inside the overview backdrop.\nlayer-rule {\n    match namespace=\"^wallpaper$\"\n\n    place-within-backdrop true\n}\n```\n\n#### `baba-is-float`\n\n<sup>Since: 25.05</sup>\n\nMake your layer surfaces FLOAT up and down.\n\nThis is a natural extension of the [April Fools' 2025 feature](./Configuration:-Window-Rules.md#baba-is-float).\n\n```kdl\n// Make fuzzel FLOAT.\nlayer-rule {\n    match namespace=\"^launcher$\"\n\n    baba-is-float true\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Layout.md",
    "content": "### Overview\n\nIn the `layout {}` section you can change various settings that influence how windows are positioned and sized.\n\nHere are the contents of this section at a glance:\n\n```kdl\nlayout {\n    gaps 16\n    center-focused-column \"never\"\n    always-center-single-column\n    empty-workspace-above-first\n    default-column-display \"tabbed\"\n    background-color \"#003300\"\n\n    preset-column-widths {\n        proportion 0.33333\n        proportion 0.5\n        proportion 0.66667\n    }\n\n    default-column-width { proportion 0.5; }\n\n    preset-window-heights {\n        proportion 0.33333\n        proportion 0.5\n        proportion 0.66667\n    }\n\n    focus-ring {\n        // off\n        on\n        width 4\n        active-color \"#7fc8ff\"\n        inactive-color \"#505050\"\n        urgent-color \"#9b0000\"\n        // active-gradient from=\"#80c8ff\" to=\"#bbddff\" angle=45\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\"\n        // urgent-gradient from=\"#800\" to=\"#a33\" angle=45\n    }\n\n    border {\n        off\n        // on\n        width 4\n        active-color \"#ffc87f\"\n        inactive-color \"#505050\"\n        urgent-color \"#9b0000\"\n        // active-gradient from=\"#ffbb66\" to=\"#ffc880\" angle=45 relative-to=\"workspace-view\"\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\" in=\"srgb-linear\"\n        // urgent-gradient from=\"#800\" to=\"#a33\" angle=45\n    }\n\n    shadow {\n        off\n        // on\n        softness 30\n        spread 5\n        offset x=0 y=5\n        draw-behind-window true\n        color \"#00000070\"\n        // inactive-color \"#00000054\"\n    }\n\n    tab-indicator {\n        // off\n        on\n        hide-when-single-tab\n        place-within-column\n        gap 5\n        width 4\n        length total-proportion=1.0\n        position \"right\"\n        gaps-between-tabs 2\n        corner-radius 8\n        active-color \"red\"\n        inactive-color \"gray\"\n        urgent-color \"blue\"\n        // active-gradient from=\"#80c8ff\" to=\"#bbddff\" angle=45\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\"\n        // urgent-gradient from=\"#800\" to=\"#a33\" angle=45\n    }\n\n    insert-hint {\n        // off\n        on\n        color \"#ffc87f80\"\n        // gradient from=\"#ffbb6680\" to=\"#ffc88080\" angle=45 relative-to=\"workspace-view\"\n    }\n\n    struts {\n        // left 64\n        // right 64\n        // top 64\n        // bottom 64\n    }\n}\n```\n\n<sup>Since: 25.11</sup> You can override these settings for specific [outputs](./Configuration:-Outputs.md#layout-config-overrides) and [named workspaces](./Configuration:-Named-Workspaces.md#layout-config-overrides).\n\n### `gaps`\n\nSet gaps around (inside and outside) windows in logical pixels.\n\n<sup>Since: 0.1.7</sup> You can use fractional values.\nThe value will be rounded to physical pixels according to the scale factor of every output.\nFor example, `gaps 0.5` on an output with `scale 2` will result in one physical-pixel wide gaps.\n\n<sup>Since: 0.1.8</sup> You can emulate \"inner\" vs. \"outer\" gaps with negative `struts` values (see the struts section below).\n\n```kdl\nlayout {\n    gaps 16\n}\n```\n\n### `center-focused-column`\n\nWhen to center a column when changing focus.\nThis can be set to:\n\n- `\"never\"`: no special centering, focusing an off-screen column will scroll it to the left or right edge of the screen. This is the default.\n- `\"always\"`, the focused column will always be centered.\n- `\"on-overflow\"`, focusing a column will center it if it doesn't fit on screen together with the previously focused column.\n\n```kdl\nlayout {\n    center-focused-column \"always\"\n}\n```\n\n### `always-center-single-column`\n\n<sup>Since: 0.1.9</sup>\n\nIf set, niri will always center a single column on a workspace, regardless of the `center-focused-column` option.\n\n```kdl\nlayout {\n    always-center-single-column\n}\n```\n\n### `empty-workspace-above-first`\n\n<sup>Since: 25.01</sup>\n\nIf set, niri will always add an empty workspace at the very start, in addition to the empty workspace at the very end.\n\n```kdl\nlayout {\n    empty-workspace-above-first\n}\n```\n\n### `default-column-display`\n\n<sup>Since: 25.02</sup>\n\nSets the default display mode for new columns.\nCan be `normal` or `tabbed`.\n\n```kdl\n// Make all new columns tabbed by default.\nlayout {\n    default-column-display \"tabbed\"\n\n    // You may also want to hide the tab indicator\n    // when there's only a single window in a column.\n    tab-indicator {\n        hide-when-single-tab\n    }\n}\n```\n\n### `preset-column-widths`\n\nSet the widths that the `switch-preset-column-width` action (Mod+R) toggles between.\n\n`proportion` sets the width as a fraction of the output width, taking gaps into account.\nFor example, you can perfectly fit four windows sized `proportion 0.25` on an output, regardless of the gaps setting.\nThe default preset widths are <sup>1</sup>&frasl;<sub>3</sub>, <sup>1</sup>&frasl;<sub>2</sub> and <sup>2</sup>&frasl;<sub>3</sub> of the output.\n\n`fixed` sets the window width in logical pixels exactly.\n\n```kdl\nlayout {\n    // Cycle between 1/3, 1/2, 2/3 of the output, and a fixed 1280 logical pixels.\n    preset-column-widths {\n        proportion 0.33333\n        proportion 0.5\n        proportion 0.66667\n        fixed 1280\n    }\n}\n```\n\n### `default-column-width`\n\nSet the default width of the new windows.\n\nThe syntax is the same as in `preset-column-widths` above.\n\n```kdl\nlayout {\n    // Open new windows sized 1/3 of the output.\n    default-column-width { proportion 0.33333; }\n}\n```\n\nYou can also leave the brackets empty, then the windows themselves will decide their initial width.\n\n```kdl\nlayout {\n    // New windows decide their initial width themselves.\n    default-column-width {}\n}\n```\n\n> [!NOTE]\n> `default-column-width {}` causes niri to send a (0, H) size in the initial configure request.\n>\n> This is a bit [unclearly defined](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/155) in the Wayland protocol, so some clients may misinterpret it.\n> Either way, `default-column-width {}` is most useful for specific windows, in form of a [window rule](./Configuration:-Window-Rules.md#default-column-width) with the same syntax.\n\n### `preset-window-heights`\n\n<sup>Since: 0.1.9</sup>\n\nSet the heights that the `switch-preset-window-height` action (Mod+Shift+R) toggles between.\n\n`proportion` sets the height as a fraction of the output height, taking gaps into account.\nThe default preset heights are <sup>1</sup>&frasl;<sub>3</sub>, <sup>1</sup>&frasl;<sub>2</sub> and <sup>2</sup>&frasl;<sub>3</sub> of the output.\n\n`fixed` sets the height in logical pixels exactly.\n\n```kdl\nlayout {\n    // Cycle between 1/3, 1/2, 2/3 of the output, and a fixed 720 logical pixels.\n    preset-window-heights {\n        proportion 0.33333\n        proportion 0.5\n        proportion 0.66667\n        fixed 720\n    }\n}\n```\n\n### `focus-ring` and `border`\n\nFocus ring and border are drawn around windows and indicate the active window.\nThey are very similar and have the same options.\n\nThe difference is that the focus ring is drawn only around the active window, whereas borders are drawn around all windows and affect their sizes (windows shrink to make space for the borders).\n\n| Focus Ring                | Border                |\n| ------------------------- | --------------------- |\n| ![Screenshot showing a focused image in the center row using focus ring](./img/focus-ring.png) | ![Screenshot showing a focused image in the center row using border, while top and bottom windows have the inactive color](./img/border.png) |\n\n> [!TIP]\n> By default, focus ring and border are rendered as a solid background rectangle behind windows.\n> That is, they will show up through semitransparent windows.\n> This is because windows using client-side decorations can have an arbitrary shape.\n>\n> If you don't like that, you should uncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config.\n> Niri will draw focus rings and borders *around* windows that agree to omit their client-side decorations.\n>\n> Alternatively, you can override this behavior with the [`draw-border-with-background` window rule](./Configuration:-Window-Rules.md#draw-border-with-background).\n\nFocus ring and border have the following options.\n\n```kdl\nlayout {\n    // focus-ring has the same options.\n    border {\n        // Uncomment this line to disable the border.\n        // off\n\n        // Width of the border in logical pixels.\n        width 4\n\n        active-color \"#ffc87f\"\n        inactive-color \"#505050\"\n\n        // Color of the border around windows that request your attention.\n        urgent-color \"#9b0000\"\n\n        // active-gradient from=\"#ffbb66\" to=\"#ffc880\" angle=45 relative-to=\"workspace-view\"\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\" in=\"srgb-linear\"\n    }\n}\n```\n\n#### Width\n\nSet the thickness of the border in logical pixels.\n\n<sup>Since: 0.1.7</sup> You can use fractional values.\nThe value will be rounded to physical pixels according to the scale factor of every output.\nFor example, `width 0.5` on an output with `scale 2` will result in one physical-pixel thick borders.\n\n```kdl\nlayout {\n    border {\n        width 2\n    }\n}\n```\n\n#### Colors\n\nColors can be set in a variety of ways:\n\n- CSS named colors: `\"red\"`\n- RGB hex: `\"#rgb\"`, `\"#rgba\"`, `\"#rrggbb\"`, `\"#rrggbbaa\"`\n- CSS-like notation: `\"rgb(255, 127, 0)\"`, `\"rgba()\"`, `\"hsl()\"` and a few others.\n\n`active-color` is the color of the focus ring / border around the active window, and `inactive-color` is the color of the focus ring / border around all other windows.\n\nThe *focus ring* is only drawn around the active window on each monitor, so with a single monitor you will never see its `inactive-color`.\nYou will see it if you have multiple monitors, though.\n\nThere's also a *deprecated* syntax for setting colors with four numbers representing R, G, B and A: `active-color 127 200 255 255`.\n\n#### Gradients\n\nSimilarly to colors, you can set `active-gradient` and `inactive-gradient`, which will take precedence.\n\nGradients are rendered the same as CSS [`linear-gradient(angle, from, to)`](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient).\nThe angle works the same as in `linear-gradient`, and is optional, defaulting to `180` (top-to-bottom gradient).\nYou can use any CSS linear-gradient tool on the web to set these up, like [css-gradient.com](https://www.css-gradient.com/).\n\n```kdl\nlayout {\n    focus-ring {\n        active-gradient from=\"#80c8ff\" to=\"#bbddff\" angle=45\n    }\n}\n```\n\nGradients can be colored relative to windows individually (the default), or to the whole view of the workspace.\nTo do that, set `relative-to=\"workspace-view\"`.\nHere's a visual example:\n\n| Default                          | `relative-to=\"workspace-view\"`                      |\n| -------------------------------- | --------------------------------------------------- |\n| ![Screenshot displaying 4 windows, each with individual gradient borders](./img/gradients-default.png) | ![Screenshot displaying 4 windows, with a shared gradient across their borders](./img/gradients-relative-to-workspace-view.png) |\n\n```kdl\nlayout {\n    border {\n        active-gradient from=\"#ffbb66\" to=\"#ffc880\" angle=45 relative-to=\"workspace-view\"\n        inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\"\n    }\n}\n```\n\n<sup>Since: 0.1.8</sup> You can set the gradient interpolation color space using syntax like `in=\"srgb-linear\"` or `in=\"oklch longer hue\"`.\nSupported color spaces are:\n\n- `srgb` (the default),\n- `srgb-linear`,\n- `oklab`,\n- `oklch` with `shorter hue` or `longer hue` or `increasing hue` or `decreasing hue`.\n\nThey are rendered the same as CSS.\nFor 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)`.\n\n![Screenshot showing a window with a border using a gradient in the oklch color space](./img/gradients-oklch.png)\n\n```kdl\nlayout {\n    border {\n        active-gradient from=\"#f00f\" to=\"#0f05\" angle=45 in=\"oklch longer hue\"\n    }\n}\n```\n\n### `shadow`\n\n<sup>Since: 25.02</sup>\n\nShadow rendered behind a window.\n\nSet `on` to enable the shadow.\n\n`softness` controls the shadow softness/size in logical pixels, same as [CSS box-shadow] *blur radius*.\nSetting `softness 0` will give you hard shadows.\n\n`spread` is the distance to expand the window rectangle in logical pixels, same as CSS box-shadow spread.\n<sup>Since: 25.05</sup> Spread can be negative.\n\n`offset` moves the shadow relative to the window in logical pixels, same as CSS box-shadow offset.\nFor example, `offset x=2 y=2` will move the shadow 2 logical pixels downwards and to the right.\n\nSet `draw-behind-window` to `true` to make shadows draw behind the window rather than just around it.\nNote that niri has no way of knowing about the CSD window corner radius.\nIt has to assume that windows have square corners, leading to shadow artifacts inside the CSD rounded corners.\nThis setting fixes those artifacts.\n\nHowever, instead you may want to set `prefer-no-csd` and/or `geometry-corner-radius`.\nThen, niri will know the corner radius and draw the shadow correctly, without having to draw it behind the window.\nThese will also remove client-side shadows if the window draws any.\n\n`color` is the shadow color and opacity.\n\n`inactive-color` lets you override the shadow color for inactive windows; by default, a more transparent `color` is used.\n\nShadow drawing will follow the window corner radius set with the [`geometry-corner-radius` window rule](./Configuration:-Window-Rules.md#geometry-corner-radius).\n\n> [!NOTE]\n> Currently, shadow drawing only supports matching radius for all corners. If you set `geometry-corner-radius` to four values instead of one, the first (top-left) corner radius will be used for shadows.\n\n```kdl\n// Enable shadows.\nlayout {\n    shadow {\n        on\n    }\n}\n\n// Also ask windows to omit client-side decorations, so that\n// they don't draw their own window shadows.\nprefer-no-csd\n```\n\n[CSS box-shadow]: https://developer.mozilla.org/en-US/docs/Web/CSS/box-shadow\n\n### `tab-indicator`\n\n<sup>Since: 25.02</sup>\n\nControls the appearance of the tab indicator that appears next to columns in tabbed display mode.\n\nSet `off` to hide the tab indicator.\n\nSet `hide-when-single-tab` to hide the indicator for tabbed columns that only have a single window.\n\nSet `place-within-column` to put the tab indicator \"within\" the column, rather than outside.\nThis will include it in column sizing and avoid overlaying adjacent columns.\n\n`gap` sets the gap between the tab indicator and the window in logical pixels.\nThe gap can be negative, this will put the tab indicator on top of the window.\n\n`width` sets the thickness of the indicator in logical pixels.\n\n`length` controls the length of the indicator.\nSet the `total-proportion` property to make tabs take up this much length relative to the window size.\nBy default, the tab indicator has length equal to half of the window size, or `length total-proportion=0.5`.\n\n`position` sets the position of the tab indicator relative to the window.\nIt can be `left`, `right`, `top`, or `bottom`.\n\n`gaps-between-tabs` controls the gap between individual tabs in logical pixels.\n\n`corner-radius` sets the rounded corner radius for tabs in the indicator in logical pixels.\nWhen `gaps-between-tabs` is zero, only the first and the last tabs have rounded corners, otherwise all tabs do.\n\n`active-color`, `inactive-color`, `urgent-color`, `active-gradient`, `inactive-gradient`, `urgent-gradient` let you override the colors for the tabs.\nThey have the same semantics as the border and focus ring colors and gradients.\n\nTab colors are picked in this order:\n\n1. Colors from the `tab-indicator` window rule, if set.\n1. Colors from the `tab-indicator` layout options, if set (you're here).\n1. If neither are set, niri picks the color matching the window border or focus ring, whichever one is active.\n\n```kdl\n// Make the tab indicator wider and match the window height,\n// also put it at the top and within the column.\nlayout {\n    tab-indicator {\n        width 8\n        gap 8\n        length total-proportion=1.0\n        position \"top\"\n        place-within-column\n    }\n}\n```\n\n### `insert-hint`\n\n<sup>Since: 0.1.10</sup> \n\nSettings for the window insert position hint during an interactive window move.\n\n`off` disables the insert hint altogether.\n\n`color` and `gradient` let you change the color of the hint and have the same syntax as colors and gradients in border and focus ring.\n\n```kdl\nlayout {\n    insert-hint {\n        // off\n        color \"#ffc87f80\"\n        gradient from=\"#ffbb6680\" to=\"#ffc88080\" angle=45 relative-to=\"workspace-view\"\n    }\n}\n```\n\n### `struts`\n\nStruts shrink the area occupied by windows, similarly to layer-shell panels.\nYou can think of them as a kind of outer gaps.\nThey are set in logical pixels.\n\nLeft and right struts will cause the next window to the side to always peek out slightly.\nTop and bottom struts will simply add outer gaps in addition to the area occupied by layer-shell panels and regular gaps.\n\n<sup>Since: 0.1.7</sup> You can use fractional values.\nThe value will be rounded to physical pixels according to the scale factor of every output.\nFor example, `top 0.5` on an output with `scale 2` will result in one physical-pixel wide top strut.\n\n```kdl\nlayout {\n    struts {\n        left 64\n        right 64\n        top 64\n        bottom 64\n    }\n}\n```\n\n![A screenshot illustrating the effects of struts, as explained in the second paragraph in this section](./img/struts.png)\n\n<sup>Since: 0.1.8</sup> You can use negative values.\nThey will push the windows outwards, even outside the edges of the screen.\n\nYou can use negative struts with matching gaps value to emulate \"inner\" vs. \"outer\" gaps.\nFor example, use this for inner gaps without outer gaps:\n\n```kdl\nlayout {\n    gaps 16\n\n    struts {\n        left -16\n        right -16\n        top -16\n        bottom -16\n    }\n}\n```\n\n### `background-color`\n\n<sup>Since: 25.05</sup>\n\nSet the default background color that niri draws for workspaces.\nThis is visible when you're not using any background tools like swaybg.\n\n```kdl\nlayout {\n    background-color \"#003300\"\n}\n```\n\nYou can also set the color per-output [in the output config](./Configuration:-Outputs.md#layout-config-overrides).\n"
  },
  {
    "path": "docs/wiki/Configuration:-Miscellaneous.md",
    "content": "This page documents all top-level options that don't otherwise have dedicated pages.\n\nHere are all of these options at a glance:\n\n```kdl\nspawn-at-startup \"waybar\"\nspawn-at-startup \"alacritty\"\nspawn-sh-at-startup \"qs -c ~/source/qs/MyAwesomeShell\"\n\nprefer-no-csd\n\nscreenshot-path \"~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png\"\n\nenvironment {\n    QT_QPA_PLATFORM \"wayland\"\n    DISPLAY null\n}\n\ncursor {\n    xcursor-theme \"breeze_cursors\"\n    xcursor-size 48\n\n    hide-when-typing\n    hide-after-inactive-ms 1000\n}\n\noverview {\n    zoom 0.5\n    backdrop-color \"#262626\"\n\n    workspace-shadow {\n        // off\n        softness 40\n        spread 10\n        offset x=0 y=10\n        color \"#00000050\"\n    }\n}\n\nxwayland-satellite {\n    // off\n    path \"xwayland-satellite\"\n}\n\nclipboard {\n    disable-primary\n}\n\nhotkey-overlay {\n    skip-at-startup\n    hide-not-bound\n}\n\nconfig-notification {\n    disable-failed\n}\n```\n\n### `spawn-at-startup`\n\nAdd lines like this to spawn processes at niri startup.\n\n`spawn-at-startup` accepts a path to the program binary as the first argument, followed by arguments to the program.\n\nThis option works the same way as the [`spawn` key binding action](./Configuration:-Key-Bindings.md#spawn), so please read about all its subtleties there.\n\n```kdl\nspawn-at-startup \"waybar\"\nspawn-at-startup \"alacritty\"\n```\n\nNote that running niri as a systemd session supports xdg-desktop-autostart out of the box, which may be more convenient to use.\nThanks to this, apps that you configured to autostart in GNOME will also \"just work\" in niri, without any manual `spawn-at-startup` configuration.\n\n### `spawn-sh-at-startup`\n\n<sup>Since: 25.08</sup>\n\nAdd lines like this to run shell commands at niri startup.\n\nThe argument is a single string that is passed verbatim to `sh`.\nYou can use shell variables, pipelines, `~` expansion and everything else as expected.\n\nSee detailed description in the docs for the [`spawn-sh` key binding action](./Configuration:-Key-Bindings.md#spawn-sh).\n\n```kdl\n// Pass all arguments in the same string.\nspawn-sh-at-startup \"qs -c ~/source/qs/MyAwesomeShell\"\n```\n\n### `prefer-no-csd`\n\nThis flag will make niri ask the applications to omit their client-side decorations.\n\nIf an application will specifically ask for CSD, the request will be honored.\nAdditionally, clients will be informed that they are tiled, removing some rounded corners.\n\nWith `prefer-no-csd` set, applications that negotiate server-side decorations through the xdg-decoration protocol will have focus ring and border drawn around them *without* a solid colored background.\n\n> [!NOTE]\n> Unlike most other options, changing `prefer-no-csd` will not entirely affect already running applications.\n> It will make some windows rectangular, but won't remove the title bars.\n> This mainly has to do with niri working around a [bug in SDL2](https://github.com/libsdl-org/SDL/issues/8173) that prevents SDL2 applications from starting.\n>\n> Restart applications after changing `prefer-no-csd` in the config to fully apply it.\n\n```kdl\nprefer-no-csd\n```\n\n### `screenshot-path`\n\nSet the path where screenshots are saved.\nA `~` at the front will be expanded to the home directory.\n\nThe path is formatted with `strftime(3)` to give you the screenshot date and time.\n\nNiri will create the last folder of the path if it doesn't exist.\n\n```kdl\nscreenshot-path \"~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png\"\n```\n\nYou can also set this option to `null` to disable saving screenshots to disk.\n\n```kdl\nscreenshot-path null\n```\n\n### `environment`\n\nOverride environment variables for processes spawned by niri.\n\n```kdl\nenvironment {\n    // Set a variable like this:\n    // QT_QPA_PLATFORM \"wayland\"\n\n    // Remove a variable by using null as the value:\n    // DISPLAY null\n}\n```\n\nNote that these variables do not propagate to the systemd global environment, so tools and applications started by systemd do not see them.\nIn particular, if you start a desktop shell like DankMaterialShell through systemd, then use its built-in application launcher, the apps won't see these environment variables.\n\nIf you want all processes to see the environment variables, you can set them in your login shell config instead (i.e. `~/.bash_profile`).\nThe `niri-session` shell script runs through the login shell and imports all environment variables to systemd before starting niri.\nKeep in mind that all compositors will see variables set in the login shell, not just niri.\n\n### `cursor`\n\nChange the theme and size of the cursor as well as set the `XCURSOR_THEME` and `XCURSOR_SIZE` environment variables.\n\n```kdl\ncursor {\n    xcursor-theme \"breeze_cursors\"\n    xcursor-size 48\n}\n```\n\n#### `hide-when-typing`\n\n<sup>Since: 0.1.10</sup>\n\nIf set, hides the cursor when pressing a key on the keyboard.\n\n> [!NOTE]\n> This setting might interfere with games running in Wine in native Wayland mode that use mouselook, such as first-person games.\n> If your character's point of view jumps down when you press a key and move the mouse simultaneously, try disabling this setting.\n\n```kdl\ncursor {\n    hide-when-typing\n}\n```\n\n#### `hide-after-inactive-ms`\n\n<sup>Since: 0.1.10</sup>\n\nIf set, the cursor will automatically hide once this number of milliseconds passes since the last cursor movement.\n\n```kdl\ncursor {\n    // Hide the cursor after one second of inactivity.\n    hide-after-inactive-ms 1000\n}\n```\n\n### `overview`\n\n<sup>Since: 25.05</sup>\n\nSettings for the [Overview](./Overview.md).\n\n#### `zoom`\n\nControl how much the workspaces zoom out in the overview.\n`zoom` ranges from 0 to 0.75 where lower values make everything smaller.\n\n```kdl\n// Make workspaces four times smaller than normal in the overview.\noverview {\n    zoom 0.25\n}\n```\n\n#### `backdrop-color`\n\nSet the backdrop color behind workspaces in the overview.\nThe backdrop is also visible between workspaces when switching.\n\nThe alpha channel for this color will be ignored.\n\n```kdl\n// Make the backdrop light.\noverview {\n    backdrop-color \"#777777\"\n}\n```\n\nYou can also set the color per-output [in the output config](./Configuration:-Outputs.md#backdrop-color).\n\n#### `workspace-shadow`\n\nControl the shadow behind workspaces visible in the overview.\n\nSettings here mirror the normal [`shadow` config in the layout section](./Configuration:-Layout.md#shadow), so check the documentation there.\n\nWorkspace shadows are configured for a workspace size normalized to 1080 pixels tall, then zoomed out together with the workspace.\nPractically, this means that you'll want bigger spread, offset, and softness compared to window shadows.\n\n```kdl\n// Disable workspace shadows in the overview.\noverview {\n    workspace-shadow {\n        off\n    }\n}\n```\n\n### `xwayland-satellite`\n\n<sup>Since: 25.08</sup>\n\nSettings for integration with [xwayland-satellite](https://github.com/Supreeeme/xwayland-satellite).\n\nWhen 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.\nIf Xwayland dies, niri will keep watching the X11 socket and restart `xwayland-satellite` as needed.\nThis is very similar to how built-in Xwayland works in other compositors.\n\n`off` disables the integration: niri won't create an X11 socket and won't set the `DISPLAY` environment variable.\n\n`path` sets the path to the `xwayland-satellite` binary.\nBy default, it's just `xwayland-satellite`, so it's looked up like any other non-absolute program name.\n\n```kdl\n// Use a custom build of xwayland-satellite.\nxwayland-satellite {\n    path \"~/source/rs/xwayland-satellite/target/release/xwayland-satellite\"\n}\n```\n\n### `clipboard`\n\n<sup>Since: 25.02</sup>\n\nClipboard settings.\n\nSet the `disable-primary` flag to disable the primary clipboard (middle-click paste).\nToggling this flag will only apply to applications started afterward.\n\n```kdl\nclipboard {\n    disable-primary\n}\n```\n\n### `hotkey-overlay`\n\nSettings for the \"Important Hotkeys\" overlay.\n\n#### `skip-at-startup`\n\nSet the `skip-at-startup` flag if you don't want to see the hotkey help at niri startup.\n\n```kdl\nhotkey-overlay {\n    skip-at-startup\n}\n```\n\n#### `hide-not-bound`\n\n<sup>Since: 25.08</sup>\n\nBy default, niri will show the most important actions even if they aren't bound to any key, to prevent confusion.\nSet the `hide-not-bound` flag if you want to hide all actions not bound to any key.\n\n```kdl\nhotkey-overlay {\n    hide-not-bound\n}\n```\n\nYou can customize which binds the hotkey overlay shows using the [`hotkey-overlay-title` property](./Configuration:-Key-Bindings.md#custom-hotkey-overlay-titles).\n\n### `config-notification`\n\n<sup>Since: 25.08</sup>\n\nSettings for the config created/failed notification.\n\nSet the `disable-failed` flag to disable the \"Failed to parse the config file\" notification.\nFor example, if you have a custom one.\n\n```kdl\nconfig-notification {\n    disable-failed\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Named-Workspaces.md",
    "content": "### Overview\n\n<sup>Since: 0.1.6</sup>\n\nYou can declare named workspaces at the top level of the config:\n\n```kdl\nworkspace \"browser\"\n\nworkspace \"chat\" {\n    open-on-output \"Some Company CoolMonitor 1234\"\n}\n```\n\nContrary to normal dynamic workspaces, named workspaces always exist, even when they have no windows.\nOtherwise, they behave like any other workspace: you can move them around, move to a different monitor, and so on.\n\nActions like `focus-workspace` or `move-column-to-workspace` can refer to workspaces by name.\nAlso, you can use an `open-on-workspace` window rule to make a window open on a specific named workspace:\n\n```kdl\n// Declare a workspace named \"chat\" that opens on the \"DP-2\" output.\nworkspace \"chat\" {\n    open-on-output \"DP-2\"\n}\n\n// Open Fractal on the \"chat\" workspace, if it runs at niri startup.\nwindow-rule {\n    match at-startup=true app-id=r#\"^org\\.gnome\\.Fractal$\"#\n    open-on-workspace \"chat\"\n}\n```\n\nNamed workspaces initially appear in the order they are declared in the config file.\nWhen editing the config while niri is running, newly declared named workspaces will appear at the very top of a monitor.\n\nIf you delete some named workspace from the config, the workspace will become normal (unnamed), and if there are no windows on it, it will be removed (as any other normal workspace).\nThere's no way to give a name to an already existing workspace, but you can simply move windows that you want to a new, empty named workspace.\n\n<sup>Since: 0.1.9</sup> `open-on-output` can now use monitor manufacturer, model, and serial.\nBefore, it could only use the connector name.\n\n<sup>Since: 25.01</sup> You can use `set-workspace-name` and `unset-workspace-name` actions to change workspace names dynamically.\n\n<sup>Since: 25.02</sup> Named workspaces no longer update/forget their original output when opening a new window on them (unnamed workspaces will keep doing that).\nThis means that named workspaces \"stick\" to their original output in more cases, reflecting their more permanent nature.\nExplicitly moving a named workspace to a different monitor will still update its original output.\n\n### Layout config overrides\n\n<sup>Since: 25.11</sup>\n\nYou can customize layout settings for named workspaces with a `layout {}` block:\n\n```kdl\nworkspace \"aesthetic\" {\n    // Layout config overrides just for this named workspace.\n    layout {\n        gaps 32\n\n        struts {\n            left 64\n            right 64\n            bottom 64\n            top 64\n        }\n\n        border {\n            on\n            width 4\n        }\n\n        // ...any other setting.\n    }\n}\n```\n\nIt accepts all the same options as [the top-level `layout {}` block](./Configuration:-Layout.md), except:\n\n- `empty-workspace-above-first`: this is an output-level setting, doesn't make sense on a workspace.\n- `insert-hint`: currently we always draw these at the output level, so it's not customizable per-workspace.\n\nIn order to unset a flag, write it with `false`, e.g.:\n\n```kdl\nlayout {\n    // Enabled globally.\n    always-center-single-column\n}\n\nworkspace \"uncentered\" {\n    layout {\n        // Unset on this workspace.\n        always-center-single-column false\n    }\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Outputs.md",
    "content": "### Overview\n\nBy default, niri will attempt to turn on all connected monitors using their preferred modes.\nYou can disable or adjust this with `output` sections.\n\nHere's what it looks like with all properties written out:\n\n```kdl\noutput \"eDP-1\" {\n    // off\n    mode \"1920x1080@120.030\"\n    scale 2.0\n    transform \"90\"\n    position x=1280 y=0\n    variable-refresh-rate // on-demand=true\n    focus-at-startup\n    backdrop-color \"#001100\"\n\n    hot-corners {\n        // off\n        top-left\n        // top-right\n        // bottom-left\n        // bottom-right\n    }\n\n    layout {\n        // ...layout settings for eDP-1...\n    }\n\n    // Custom modes. Caution: may damage your display.\n    // mode custom=true \"1920x1080@100\"\n    // modeline 173.00  1920 2048 2248 2576  1080 1083 1088 1120 \"-hsync\" \"+vsync\"\n}\n\noutput \"HDMI-A-1\" {\n    // ...settings for HDMI-A-1...\n}\n\noutput \"Some Company CoolMonitor 1234\" {\n    // ...settings for CoolMonitor...\n}\n```\n\nOutputs are matched by connector name (i.e. `eDP-1`, `HDMI-A-1`), or by monitor manufacturer, model, and serial, separated by a single space each.\nYou can find all of these by running `niri msg outputs`.\n\nUsually, the built-in monitor in laptops will be called `eDP-1`.\n\n<sup>Since: 0.1.6</sup> The output name is case-insensitive.\n\n<sup>Since: 0.1.9</sup> Outputs can be matched by manufacturer, model, and serial.\nBefore, they could be matched only by the connector name.\n\n### `off`\n\nThis flag turns off that output entirely.\n\n```kdl\n// Turn off that monitor.\noutput \"HDMI-A-1\" {\n    off\n}\n```\n\n### `mode`\n\nSet the monitor resolution and refresh rate.\n\nThe format is `<width>x<height>` or `<width>x<height>@<refresh rate>`.\nIf the refresh rate is omitted, niri will pick the highest refresh rate for the resolution.\n\nIf the mode is omitted altogether or doesn't work, niri will try to pick one automatically.\n\nRun `niri msg outputs` while inside a niri instance to list all outputs and their modes.\nThe refresh rate that you set here must match *exactly*, down to the three decimal digits, to what you see in `niri msg outputs`.\n\n```kdl\n// Set a high refresh rate for this monitor.\n// High refresh rate monitors tend to use 60 Hz as their preferred mode,\n// requiring a manual mode setting.\noutput \"HDMI-A-1\" {\n    mode \"2560x1440@143.912\"\n}\n\n// Use a lower resolution on the built-in laptop monitor\n// (for example, for testing purposes).\noutput \"eDP-1\" {\n    mode \"1280x720\"\n}\n```\n\n#### `mode custom=true`\n\n<sup>Since: 25.11</sup>\n\nYou can configure a custom mode (not offered by the monitor) by setting `custom=true`.\nIn this case, the refresh rate is mandatory.\n\nCustom modes are not guaranteed to work.\nNiri is asking the monitor to run in a mode that is not supported by the manufacturer.\nUse at your own risk.\n\n> [!CAUTION]\n> Custom modes may damage your monitor, especially if it's a CRT.\n> Follow the maximum supported limits in your monitor's instructions.\n\n```kdl\n// Use a custom mode for this display.\noutput \"HDMI-A-1\" {\n    mode custom=true \"2560x1440@143.912\"\n}\n```\n\n### `modeline`\n\n<sup>Since: 25.11</sup>\n\nDirectly configures the monitor's mode via a modeline, overriding any configured `mode`.\nThe modeline can be calculated via utilities such as [cvt](https://man.archlinux.org/man/cvt.1.en) or [gtf](https://man.archlinux.org/man/gtf.1.en).\n\nModelines are not guaranteed to work.\nNiri is asking the monitor to run in a mode not supported by the manufacturer.\nUse at your own risk.\n\n> [!CAUTION]\n> Out of spec modelines may damage your monitor, especially if it's a CRT.\n> Follow the maximum supported limits in your monitor's instructions.\n\n```kdl\n// Use a modeline for this display.\noutput \"eDP-3\" {\n    modeline 173.00  1920 2048 2248 2576  1080 1083 1088 1120 \"-hsync\" \"+vsync\"\n}\n```\n\n### `scale`\n\nSet the scale of the monitor.\n\n<sup>Since: 0.1.6</sup> If scale is unset, niri will guess an appropriate scale based on the physical dimensions and the resolution of the monitor.\n\n<sup>Since: 0.1.7</sup> You can use fractional scale values, for example `scale 1.5` for 150% scale.\n\n<sup>Since: 0.1.7</sup> Dot is no longer needed for integer scale, for example you can write `scale 2` instead of `scale 2.0`.\n\n<sup>Since: 0.1.7</sup> Scale below 0 and above 10 will now fail during config parsing. Scale was previously clamped to these values anyway.\n\n```kdl\noutput \"eDP-1\" {\n    scale 2.0\n}\n```\n\n### `transform`\n\nRotate the output counter-clockwise.\n\nValid values are: `\"normal\"`, `\"90\"`, `\"180\"`, `\"270\"`, `\"flipped\"`, `\"flipped-90\"`, `\"flipped-180\"` and `\"flipped-270\"`.\nValues with `flipped` additionally flip the output.\n\n```kdl\noutput \"HDMI-A-1\" {\n    transform \"90\"\n}\n```\n\n### `position`\n\nSet the position of the output in the global coordinate space.\n\nThis affects directional monitor actions like `focus-monitor-left`, and cursor movement.\nThe cursor can only move between directly adjacent outputs.\n\n> [!NOTE]\n> Output scale and rotation has to be taken into account for positioning: outputs are sized in logical, or scaled, pixels.\n> For example, a 3840×2160 output with scale 2.0 will have a logical size of 1920×1080, so to put another output directly adjacent to it on the right, set its x to 1920.\n> If the position is unset or results in an overlap, the output is instead placed automatically.\n\n```kdl\noutput \"HDMI-A-1\" {\n    position x=1280 y=0\n}\n```\n\n#### Automatic Positioning\n\nNiri repositions outputs from scratch every time the output configuration changes (which includes monitors disconnecting and connecting).\nThe following algorithm is used for positioning outputs.\n\n1. Collect all connected monitors and their logical sizes.\n1. Sort them by their name. This makes it so the automatic positioning does not depend on the order the monitors are connected. This is important because the connection order is non-deterministic at compositor startup.\n1. Try to place every output with explicitly configured `position`, in order. If the output overlaps previously placed outputs, place it to the right of all previously placed outputs. In this case, niri will also print a warning.\n1. Place every output without explicitly configured `position` by putting it to the right of all previously placed outputs.\n\n### `variable-refresh-rate`\n\n<sup>Since: 0.1.5</sup>\n\nThis flag enables variable refresh rate (VRR, also known as adaptive sync, FreeSync, or G-Sync), if the output supports it.\n\nYou can check whether an output supports VRR in `niri msg outputs`.\n\n> [!NOTE]\n> Some drivers have various issues with VRR.\n>\n> If the cursor moves at a low framerate with VRR, try setting the [`disable-cursor-plane` debug flag](./Configuration:-Debug-Options.md#disable-cursor-plane) and reconnecting the monitor.\n>\n> If a monitor is not detected as VRR-capable when it should, sometimes unplugging a different monitor fixes it.\n>\n> Some monitors will continuously modeset (flash black) with VRR enabled; I'm not sure if there's a way to fix it.\n\n```kdl\noutput \"HDMI-A-1\" {\n    variable-refresh-rate\n}\n```\n\n<sup>Since: 0.1.9</sup> You can also set the `on-demand=true` property, which will only enable VRR when this output shows a window matching the `variable-refresh-rate` window rule.\nThis is helpful to avoid various issues with VRR, since it can be disabled most of the time, and only enabled for specific windows, like games or video players.\n\n```kdl\noutput \"HDMI-A-1\" {\n    variable-refresh-rate on-demand=true\n}\n```\n\n### `focus-at-startup`\n\n<sup>Since: 25.05</sup>\n\nFocus this output by default when niri starts.\n\nIf multiple outputs with `focus-at-startup` are connected, they are prioritized in the order that they appear in the config.\n\nWhen none of the connected outputs are explicitly `focus-at-startup`, niri will focus the first one sorted by name (same output sorting as used elsewhere in niri).\n\n```kdl\n// Focus HDMI-A-1 by default.\noutput \"HDMI-A-1\" {\n    focus-at-startup\n}\n\n// ...if HDMI-A-1 wasn't connected, focus DP-2 instead.\noutput \"DP-2\" {\n    focus-at-startup\n}\n```\n\n### `background-color`\n\n<sup>Since: 0.1.8</sup>\n\nSet the background color that niri draws for workspaces on this output.\nThis is visible when you're not using any background tools like swaybg.\n\n<sup>Until: 25.05</sup> The alpha channel for this color will be ignored.\n\n<sup>Since: 25.11</sup> This setting is deprecated, set `background-color` in the [output `layout {}` block](#layout-config-overrides) instead.\n\n```kdl\noutput \"HDMI-A-1\" {\n    background-color \"#003300\"\n}\n```\n\n### `backdrop-color`\n\n<sup>Since: 25.05</sup>\n\nSet the backdrop color that niri draws for this output.\nThis is visible between workspaces or in the overview.\n\nThe alpha channel for this color will be ignored.\n\n```kdl\noutput \"HDMI-A-1\" {\n    backdrop-color \"#001100\"\n}\n```\n\n### `hot-corners`\n\n<sup>Since: 25.11</sup>\n\nCustomize the hot corners for this output.\nBy default, hot corners [in the gestures settings](./Configuration:-Gestures.md#hot-corners) are used for all outputs.\n\nHot corners toggle the overview when you put your mouse at the very corner of a monitor.\n\n`off` will disable the hot corners on this output, and writing specific corners will enable only those hot corners on this output.\n\n```kdl\n// Enable the bottom-left and bottom-right hot corners on HDMI-A-1.\noutput \"HDMI-A-1\" {\n    hot-corners {\n        bottom-left\n        bottom-right\n    }\n}\n\n// Disable the hot corners on DP-2.\noutput \"DP-2\" {\n    hot-corners {\n        off\n    }\n}\n```\n\n### Layout config overrides\n\n<sup>Since: 25.11</sup>\n\nYou can customize layout settings for an output with a `layout {}` block:\n\n```kdl\noutput \"SomeCompany VerticalMonitor 1234\" {\n    transform \"90\"\n\n    // Layout config overrides just for this output.\n    layout {\n        default-column-width { proportion 1.0; }\n\n        // ...any other setting.\n    }\n}\n\noutput \"SomeCompany UltrawideMonitor 1234\" {\n    // Narrower proportions and more presets for an ultrawide.\n    layout {\n        default-column-width { proportion 0.25; }\n\n        preset-column-widths {\n            proportion 0.2\n            proportion 0.25\n            proportion 0.5\n            proportion 0.75\n            proportion 0.8\n        }\n    }\n}\n```\n\nIt accepts all the same options as [the top-level `layout {}` block](./Configuration:-Layout.md).\n\nIn order to unset a flag, write it with `false`, e.g.:\n\n```kdl\nlayout {\n    // Enabled globally.\n    always-center-single-column\n}\n\noutput \"eDP-1\" {\n    layout {\n        // Unset on this output.\n        always-center-single-column false\n    }\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Overview.md",
    "content": "This wiki page has moved to: [Introduction](./Configuration:-Introduction.md).\n"
  },
  {
    "path": "docs/wiki/Configuration:-Recent-Windows.md",
    "content": "### Overview\n\n<sup>Since: 25.11</sup>\n\nIn this section you can configure the recent windows switcher (Alt-Tab).\n\nHere is an outline of the available settings and their default values:\n\n```kdl\nrecent-windows {\n    // off\n    debounce-ms 750\n\n    open-delay-ms 150\n\n    highlight {\n        active-color \"#999999ff\"\n        urgent-color \"#ff9999ff\"\n        padding 30\n        corner-radius 0\n    }\n\n    previews {\n        max-height 480\n        max-scale 0.5\n    }\n\n    binds {\n        Alt+Tab         { next-window; }\n        Alt+Shift+Tab   { previous-window; }\n        Alt+grave       { next-window     filter=\"app-id\"; }\n        Alt+Shift+grave { previous-window filter=\"app-id\"; }\n\n        Mod+Tab         { next-window; }\n        Mod+Shift+Tab   { previous-window; }\n        Mod+grave       { next-window     filter=\"app-id\"; }\n        Mod+Shift+grave { previous-window filter=\"app-id\"; }\n    }\n}\n```\n\n`off` disables the recent windows switcher altogether.\n\n### `debounce-ms`\n\nDelay, in milliseconds, between the window receiving focus and getting \"committed\" to the recent windows list.\n\nWhen you want to focus some window, you might end up focusing some unrelated windows on the way:\n\n- with keyboard navigation, the windows between your current one and the target one;\n- with [`focus-follows-mouse`](./Configuration:-Input.md#focus-follows-mouse), the windows you happen to cross with the mouse pointer on the way to the target window.\n\nThe debounce delay prevents those intermediate windows from polluting the recent windows list.\n\nNote that some actions, like keyboard input into the target window, will skip this delay and commit the window to the list immediately.\nThis way, the recent windows list stays responsive while not getting polluted too much with unintended windows.\n\nIf you want windows to appear in recent windows right away, including intermediate windows, you can reduce the delay or set it to zero:\n\n```kdl\nrecent-windows {\n    // Commit windows to the recent windows list as soon as they're focused,\n    // with no debounce delay.\n    debounce-ms 0\n}\n```\n\n### `open-delay-ms`\n\nDelay, in milliseconds, between pressing the Alt-Tab bind and the recent windows switcher visually appearing on screen.\n\nThe switcher is delayed by default so that quickly tapping Alt-Tab to switch windows wouldn't cause annoying fullscreen visual changes.\n\n```kdl\nrecent-windows {\n    // Make the switcher appear instantly.\n    open-delay-ms 0\n}\n```\n\n### `highlight`\n\nControls the highlight behind the focused window preview in the recent windows switcher.\n\n- `active-color`: normal color of the focused window highlight.\n- `urgent-color`: color of an urgent focused window highlight, also visible in a darker shade on unfocused windows.\n- `padding`: padding of the highlight around the window preview, in logical pixels.\n- `corner-radius`: corner radius of the highlight.\n\n```kdl\nrecent-windows {\n    // Round the corners on the highlight.\n    highlight {\n        corner-radius 14\n    }\n}\n```\n\n### `previews`\n\nControls the window previews in the switcher.\n\n- `max-scale`: maximum scale of the window previews.\nWindows cannot be scaled bigger than this value.\n- `max-height`: maximum height of the window previews.\nFurther limits the size of the previews in order to occupy less space on large monitors.\n\nOn smaller monitors, the previews will be primarily limited by `max-scale`, and on larger monitors they will be primarily limited by `max-height`.\n\nThe `max-scale` limit is imposed twice: on the final window scale, and on the window height which cannot exceed `monitor height × max scale`.\n\n```kdl\nrecent-windows {\n    // Make the previews smaller to fit more on screen.\n    previews {\n        max-height 320\n    }\n}\n```\n\n```kdl\nrecent-windows {\n    // Make the previews larger to see the window contents.\n    previews {\n        max-height 1080\n        max-scale 0.75\n    }\n}\n```\n\n### `binds`\n\nConfigure binds that open and navigate the recent windows switcher.\n\nThe defaults are <kbd>Alt</kbd><kbd>Tab</kbd> / <kbd>Mod</kbd><kbd>Tab</kbd> to switch across all windows, and <kbd>Alt</kbd><kbd>\\`</kbd> / <kbd>Mod</kbd><kbd>\\`</kbd> to switch between windows of the current application.\nAdding <kbd>Shift</kbd> will switch windows backwards.\n\nAdding the recent windows `binds {}` section to your config removes all default binds.\nYou can copy the ones you need from the summary at the top of this wiki page.\n\n```kdl\nrecent-windows {\n    // Even an empty binds {} section will remove all default binds.\n    binds {\n    }\n}\n```\n\nThe available actions are `next-window` and `previous-window`.\nThey can optionally have the following properties:\n\n- `filter=\"app-id\"`: filters the switcher to the windows of the currently selected application, as determined by the Wayland app ID.\n- `scope=\"all\"`, `scope=\"output\"`, `scope=\"workspace\"`: sets the pre-selected scope when this bind is used to open the recent windows switcher.\n\n```kdl\nrecent-windows {\n    // Pre-select the \"Output\" scope when switching windows.\n    binds {\n        Mod+Tab         { next-window     scope=\"output\"; }\n        Mod+Shift+Tab   { previous-window scope=\"output\"; }\n        Mod+grave       { next-window     scope=\"output\" filter=\"app-id\"; }\n        Mod+Shift+grave { previous-window scope=\"output\" filter=\"app-id\"; }\n    }\n}\n```\n\nThe recent windows binds have lower precedence than the [normal binds](./Configuration:-Key-Bindings.md), meaning that if you have <kbd>Alt</kbd><kbd>Tab</kbd> bound to something else in the normal binds, the `recent-windows` bind won't work.\nIn this case, you can remove the conflicting normal bind.\n\nAll binds in this section must have a modifier key like <kbd>Alt</kbd> or <kbd>Mod</kbd> because the recent windows switcher remains open only while you hold any modifier key.\n\n#### Bindings inside the switcher\n\nWhen the switcher is open, some hardcoded binds are available:\n\n- <kbd>Escape</kbd> cancels the switcher.\n- <kbd>Enter</kbd> closes the switcher confirming the current window.\n- <kbd>A</kbd>, <kbd>W</kbd>, <kbd>O</kbd> select a specific scope.\n- <kbd>S</kbd> cycles between scopes, as indicated by the panel at the top.\n- <kbd>←</kbd>, <kbd>→</kbd>, <kbd>Home</kbd>, <kbd>End</kbd> move the selection directionally.\n\nAdditionally, certain regular binds will automatically work in the switcher:\n\n- focus column left/right and their variants: will move the selection left/right inside the switcher.\n- focus column first/last: will move the selection to the first or last window.\n- close window: will close the window currently focused in the switcher.\n- screenshot: will open the screenshot UI.\n\nThe way this works is by finding all regular binds corresponding to these actions and taking just the trigger key without modifiers.\nFor example, if you have <kbd>Mod</kbd><kbd>Shift</kbd><kbd>C</kbd> bound to `close-window`, in the window switcher pressing <kbd>C</kbd> on its own will close the window.\n\nThis way we don't need to hardcode things like HJKL directional movements.\nIf you have, say, Colemak-DH MNEI binds instead, they will work for you in the window switcher (as long as they don't conflict with the hardcoded ones).\n"
  },
  {
    "path": "docs/wiki/Configuration:-Switch-Events.md",
    "content": "### Overview\n\n<sup>Since: 0.1.10</sup>\n\nSwitch event bindings are declared in the `switch-events {}` section of the config.\n\nHere are all the events that you can bind at a glance:\n\n```kdl\nswitch-events {\n    lid-close { spawn \"notify-send\" \"The laptop lid is closed!\"; }\n    lid-open { spawn \"notify-send\" \"The laptop lid is open!\"; }\n    tablet-mode-on { spawn \"bash\" \"-c\" \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true\"; }\n    tablet-mode-off { spawn \"bash\" \"-c\" \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false\"; }\n}\n```\n\nThe syntax is similar to key bindings.\nCurrently, only the [`spawn` action](./Configuration:-Key-Bindings.md#spawn) are supported.\n\n> [!NOTE]\n> In contrast to key bindings, switch event bindings are *always* executed, even when the session is locked.\n\n### `lid-close`, `lid-open`\n\nThese events correspond to closing and opening of the laptop lid.\n\nNote that niri will already automatically turn the internal laptop monitor on and off in accordance with the laptop lid.\n\n```kdl\nswitch-events {\n    lid-close { spawn \"notify-send\" \"The laptop lid is closed!\"; }\n    lid-open { spawn \"notify-send\" \"The laptop lid is open!\"; }\n}\n```\n\n### `tablet-mode-on`, `tablet-mode-off`\n\nThese events trigger when a convertible laptop goes into or out of tablet mode.\nIn tablet mode, the keyboard and mouse are usually inaccessible, so you can use these events to activate the on-screen keyboard.\n\n> [!NOTE]\n> The commands below are just examples, you will need to provide your own on-screen keyboard, such as [sysboard](https://github.com/System64fumo/sysboard) or [wvkbd](https://github.com/jjsullivan5196/wvkbd).\n\n```kdl\nswitch-events {\n    tablet-mode-on { spawn \"bash\" \"-c\" \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true\"; }\n    tablet-mode-off { spawn \"bash\" \"-c\" \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false\"; }\n}\n```\n"
  },
  {
    "path": "docs/wiki/Configuration:-Window-Rules.md",
    "content": "### Overview\n\nWindow rules let you adjust behavior for individual windows.\nThey have `match` and `exclude` directives that control which windows the rule should apply to, and a number of properties that you can set.\n\nWindow rules are processed in order of appearance in the config file.\nThis means that you can put more generic rules first, then override them for specific windows later.\nFor example:\n\n```kdl\n// Set open-maximized to true for all windows.\nwindow-rule {\n    open-maximized true\n}\n\n// Then, for Alacritty, set open-maximized back to false.\nwindow-rule {\n    match app-id=\"Alacritty\"\n    open-maximized false\n}\n```\n\n> [!TIP]\n> In general, you cannot \"unset\" a property in a later rule, only set it to a different value.\n> Use the `exclude` directives to avoid applying a rule for specific windows.\n\nHere are all matchers and properties that a window rule could have:\n\n```kdl\nwindow-rule {\n    match title=\"Firefox\"\n    match app-id=\"Alacritty\"\n    match is-active=true\n    match is-focused=false\n    match is-active-in-column=true\n    match is-floating=true\n    match is-window-cast-target=true\n    match is-urgent=true\n    match at-startup=true\n\n    // Properties that apply once upon window opening.\n    default-column-width { proportion 0.75; }\n    default-window-height { fixed 500; }\n    open-on-output \"Some Company CoolMonitor 1234\"\n    open-on-workspace \"chat\"\n    open-maximized true\n    open-maximized-to-edges true\n    open-fullscreen true\n    open-floating true\n    open-focused false\n\n    // Properties that apply continuously.\n    draw-border-with-background false\n    opacity 0.5\n    block-out-from \"screencast\"\n    // block-out-from \"screen-capture\"\n    variable-refresh-rate true\n    default-column-display \"tabbed\"\n    default-floating-position x=100 y=200 relative-to=\"bottom-left\"\n    scroll-factor 0.75\n\n    focus-ring {\n        // off\n        on\n        width 4\n        active-color \"#7fc8ff\"\n        inactive-color \"#505050\"\n        urgent-color \"#9b0000\"\n        // active-gradient from=\"#80c8ff\" to=\"#bbddff\" angle=45\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\"\n        // urgent-gradient from=\"#800\" to=\"#a33\" angle=45\n    }\n\n    border {\n        // Same as focus-ring.\n    }\n\n    shadow {\n        // on\n        off\n        softness 40\n        spread 5\n        offset x=0 y=5\n        draw-behind-window true\n        color \"#00000064\"\n        // inactive-color \"#00000064\"\n    }\n\n    tab-indicator {\n        active-color \"red\"\n        inactive-color \"gray\"\n        urgent-color \"blue\"\n        // active-gradient from=\"#80c8ff\" to=\"#bbddff\" angle=45\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\"\n        // urgent-gradient from=\"#800\" to=\"#a33\" angle=45\n    }\n\n    geometry-corner-radius 12\n    clip-to-geometry true\n    tiled-state true\n    baba-is-float true\n\n    min-width 100\n    max-width 200\n    min-height 300\n    max-height 300\n}\n```\n\n### Window Matching\n\nEach window rule can have several `match` and `exclude` directives.\nIn order for the rule to apply, a window needs to match *any* of the `match` directives, and *none* of the `exclude` directives.\n\n```kdl\nwindow-rule {\n    // Match all Telegram windows...\n    match app-id=r#\"^org\\.telegram\\.desktop$\"#\n\n    // ...except the media viewer window.\n    exclude title=\"^Media viewer$\"\n\n    // Properties to apply.\n    open-on-output \"HDMI-A-1\"\n}\n```\n\nMatch and exclude directives have the same syntax.\nThere can be multiple *matchers* in one directive, then the window should match all of them for the directive to apply.\n\n```kdl\nwindow-rule {\n    // Match Firefox windows with Gmail in title.\n    match app-id=\"firefox\" title=\"Gmail\"\n}\n\nwindow-rule {\n    // Match Firefox, but only when it is active...\n    match app-id=\"firefox\" is-active=true\n\n    // ...or match Telegram...\n    match app-id=r#\"^org\\.telegram\\.desktop$\"#\n\n    // ...but don't match the Telegram media viewer.\n    // If you open a tab in Firefox titled \"Media viewer\",\n    // it will not be excluded because it doesn't match the app-id\n    // of this exclude directive.\n    exclude app-id=r#\"^org\\.telegram\\.desktop$\"# title=\"Media viewer\"\n}\n```\n\nLet's look at the matchers in more detail.\n\n#### `title` and `app-id`\n\nThese are regular expressions that should match anywhere in the window title and app ID respectively.\nYou can read about the supported regular expression syntax [here](https://docs.rs/regex/latest/regex/#syntax).\n\n```kdl\n// Match windows with title containing \"Mozilla Firefox\",\n// or windows with app ID containing \"Alacritty\".\nwindow-rule {\n    match title=\"Mozilla Firefox\"\n    match app-id=\"Alacritty\"\n}\n```\n\nRaw KDL strings can be helpful for writing out regular expressions:\n\n```kdl\nwindow-rule {\n    exclude app-id=r#\"^org\\.keepassxc\\.KeePassXC$\"#\n}\n```\n\nYou can find the title and the app ID of a window by running `niri msg pick-window` and clicking on the window in question.\n\n> [!TIP]\n> Another way to find the window title and app ID is to configure the `wlr/taskbar` module in [Waybar](https://github.com/Alexays/Waybar) to include them in the tooltip:\n> \n> ```json\n> \"wlr/taskbar\": {\n>     \"tooltip-format\": \"{title} | {app_id}\",\n> }\n> ```\n\n#### `is-active`\n\nCan be `true` or `false`.\nMatches active windows (same windows that have the active border / focus ring color).\n\nEvery workspace on the focused monitor will have one active window.\nThis means that you will usually have multiple active windows (one per workspace), and when you switch between workspaces, you can see two active windows at once.\n\n```kdl\nwindow-rule {\n    match is-active=true\n}\n```\n\n#### `is-focused`\n\nCan be `true` or `false`.\nMatches the window that has the keyboard focus.\n\nContrary to `is-active`, there can only be a single focused window.\nAlso, when opening a layer-shell application launcher or pop-up menu, the keyboard focus goes to layer-shell.\nWhile layer-shell has the keyboard focus, windows will not match this rule.\n\n```kdl\nwindow-rule {\n    match is-focused=true\n}\n```\n\n#### `is-active-in-column`\n\n<sup>Since: 0.1.6</sup>\n\nCan be `true` or `false`.\nMatches the window that is the \"active\" window in its column.\n\nContrary to `is-active`, there is always one `is-active-in-column` window in each column.\nIt is the window that was last focused in the column, i.e. the one that will gain focus if this column is focused.\n\n<sup>Since: 25.01</sup> This rule will match `true` during the initial window opening.\n\n```kdl\nwindow-rule {\n    match is-active-in-column=true\n}\n```\n\n#### `is-floating`\n\n<sup>Since: 25.01</sup>\n\nCan be `true` or `false`.\nMatches floating windows.\n\n> [!NOTE]\n> This matcher will apply only after the window is already open.\n> This means that you cannot use it to change the window opening properties like `default-window-height` or `open-on-workspace`.\n\n```kdl\nwindow-rule {\n    match is-floating=true\n}\n```\n\n#### `is-window-cast-target`\n\n<sup>Since: 25.02</sup>\n\nCan be `true` or `false`.\nMatches `true` for windows that are target of an ongoing window screencast.\n\n> [!NOTE]\n> This only matches individual-window screencasts.\n> It will not match windows that happen to be visible in a monitor screencast, for example.\n\n```kdl\n// Indicate screencasted windows with red colors.\nwindow-rule {\n    match is-window-cast-target=true\n\n    focus-ring {\n        active-color \"#f38ba8\"\n        inactive-color \"#7d0d2d\"\n    }\n\n    border {\n        inactive-color \"#7d0d2d\"\n    }\n\n    shadow {\n        color \"#7d0d2d70\"\n    }\n\n    tab-indicator {\n        active-color \"#f38ba8\"\n        inactive-color \"#7d0d2d\"\n    }\n}\n```\n\nExample:\n\n![A screenshot showing that only the is-window-cast-target=true windows receive the special border colors](https://github.com/user-attachments/assets/375b381e-3a87-4e94-8676-44404971d893)\n\n#### `is-urgent`\n\n<sup>Since: 25.05</sup>\n\nCan be `true` or `false`.\nMatches windows that request the user's attention.\n\n```kdl\nwindow-rule {\n    match is-urgent=true\n}\n```\n\n#### `at-startup`\n\n<sup>Since: 0.1.6</sup>\n\nCan be `true` or `false`.\nMatches during the first 60 seconds after starting niri.\n\nThis is useful for properties like `open-on-output` which you may want to apply only right after starting niri.\n\n```kdl\n// Open windows on the HDMI-A-1 monitor at niri startup, but not afterwards.\nwindow-rule {\n    match at-startup=true\n    open-on-output \"HDMI-A-1\"\n}\n```\n\n### Window Opening Properties\n\nThese properties apply once, when a window first opens.\n\nTo be precise, they apply at the point when niri sends the initial configure request to the window.\n\n#### `default-column-width`\n\nSet the default width for the new window.\n\nThis works for floating windows too, despite the word \"column\" in the name.\n\n```kdl\n// Give Blender and GIMP some guaranteed width on opening.\nwindow-rule {\n    match app-id=\"^blender$\"\n\n    // GIMP app ID contains the version like \"gimp-2.99\",\n    // so we only match the beginning (with ^) and not the end.\n    match app-id=\"^gimp\"\n\n    default-column-width { fixed 1200; }\n}\n```\n\n#### `default-window-height`\n\n<sup>Since: 25.01</sup>\n\nSet the default height for the new window.\n\n```kdl\n// Open the Firefox picture-in-picture window as floating with 480×270 size.\nwindow-rule {\n    match app-id=\"firefox$\" title=\"^Picture-in-Picture$\"\n\n    open-floating true\n    default-column-width { fixed 480; }\n    default-window-height { fixed 270; }\n}\n```\n\n#### `open-on-output`\n\nMake the window open on a specific output.\n\nIf such an output does not exist, the window will open on the currently focused output as usual.\n\nIf the window opens on an output that is not currently focused, the window will not be automatically focused.\n\n```kdl\n// Open Firefox and Telegram (but not its Media Viewer)\n// on a specific monitor.\nwindow-rule {\n    match app-id=\"firefox$\"\n    match app-id=r#\"^org\\.telegram\\.desktop$\"#\n    exclude app-id=r#\"^org\\.telegram\\.desktop$\"# title=\"^Media viewer$\"\n\n    open-on-output \"HDMI-A-1\"\n    // Or:\n    // open-on-output \"Some Company CoolMonitor 1234\"\n}\n```\n\n<sup>Since: 0.1.9</sup> `open-on-output` can now use monitor manufacturer, model, and serial.\nBefore, it could only use the connector name.\n\n#### `open-on-workspace`\n\n<sup>Since: 0.1.6</sup>\n\nMake the window open on a specific [named workspace](./Configuration:-Named-Workspaces.md).\n\nIf such a workspace does not exist, the window will open on the currently focused workspace as usual.\n\nIf the window opens on an output that is not currently focused, the window will not be automatically focused.\n\n```kdl\n// Open Fractal on the \"chat\" workspace.\nwindow-rule {\n    match app-id=r#\"^org\\.gnome\\.Fractal$\"#\n\n    open-on-workspace \"chat\"\n}\n```\n\n#### `open-maximized`\n\nMake the window open as a maximized column.\n\n```kdl\n// Maximize Firefox by default.\nwindow-rule {\n    match app-id=\"firefox$\"\n\n    open-maximized true\n}\n```\n\n#### `open-maximized-to-edges`\n\n<sup>Since: 25.11</sup>\n\nMake the window open [maximized to edges](./Fullscreen-and-Maximize.md).\n\n```kdl\nwindow-rule {\n    open-maximized-to-edges true\n}\n```\n\nYou can also set this to `false` to *prevent* a window from opening maximized to edges.\n\n```kdl\nwindow-rule {\n    open-maximized-to-edges false\n}\n```\n\n#### `open-fullscreen`\n\nMake the window open [fullscreen](./Fullscreen-and-Maximize.md).\n\n```kdl\nwindow-rule {\n    open-fullscreen true\n}\n```\n\nYou can also set this to `false` to *prevent* a window from opening fullscreen.\n\n```kdl\n// Make the Telegram media viewer open in windowed mode.\nwindow-rule {\n    match app-id=r#\"^org\\.telegram\\.desktop$\"# title=\"^Media viewer$\"\n\n    open-fullscreen false\n}\n```\n\n#### `open-floating`\n\n<sup>Since: 25.01</sup>\n\nMake the window open in the floating layout.\n\n```kdl\n// Open the Firefox picture-in-picture window as floating.\nwindow-rule {\n    match app-id=\"firefox$\" title=\"^Picture-in-Picture$\"\n\n    open-floating true\n}\n```\n\nYou can also set this to `false` to *prevent* a window from opening in the floating layout.\n\n```kdl\n// Open all windows in the tiling layout, overriding any auto-floating logic.\nwindow-rule {\n    open-floating false\n}\n```\n\n#### `open-focused`\n\n<sup>Since: 25.01</sup>\n\nSet this to `false` to prevent this window from being automatically focused upon opening.\n\n```kdl\n// Don't give focus to the GIMP startup splash screen.\nwindow-rule {\n    match app-id=\"^gimp\" title=\"^GIMP Startup$\"\n\n    open-focused false\n}\n```\n\nYou can also set this to `true` to focus the window, even if normally it wouldn't get auto-focused.\n\n```kdl\n// Always focus the KeePassXC-Browser unlock dialog.\n//\n// This dialog opens parented to the KeePassXC window rather than the browser,\n// so it doesn't get auto-focused by default.\nwindow-rule {\n    match app-id=r#\"^org\\.keepassxc\\.KeePassXC$\"# title=\"^Unlock Database - KeePassXC$\"\n\n    open-focused true\n}\n```\n\n### Dynamic Properties\n\nThese properties apply continuously to open windows.\n\n#### `block-out-from`\n\nYou can block out windows from xdg-desktop-portal screencasts.\nThey will be replaced with solid black rectangles.\n\nThis can be useful for password managers or messenger windows, etc.\nFor layer-shell notification pop-ups and the like, you can use a [`block-out-from` layer rule](./Configuration:-Layer-Rules.md#block-out-from).\n\n![Screenshot showing a window visible normally, but blocked out on OBS.](./img/block-out-from-screencast.png)\n\nTo preview and set up this rule, check the `preview-render` option in the debug section of the config.\n\n> [!CAUTION]\n> The window is **not** blocked out from third-party screenshot tools.\n> If you open some screenshot tool with preview while screencasting, blocked out windows **will be visible** on the screencast.\n\nThe built-in screenshot UI is not affected by this problem though.\nIf you open the screenshot UI while screencasting, you will be able to select the area to screenshot while seeing all windows normally, but on a screencast the selection UI will display with windows blocked out.\n\n```kdl\n// Block out password managers from screencasts.\nwindow-rule {\n    match app-id=r#\"^org\\.keepassxc\\.KeePassXC$\"#\n    match app-id=r#\"^org\\.gnome\\.World\\.Secrets$\"#\n\n    block-out-from \"screencast\"\n}\n```\n\nAlternatively, you can block out the window out of *all* screen captures, including third-party screenshot tools.\nThis way you avoid accidentally showing the window on a screencast when opening a third-party screenshot preview.\n\nThis setting will still let you use the interactive built-in screenshot UI, but it will block out the window from the fully automatic screenshot actions, such as `screenshot-screen` and `screenshot-window`.\nThe reasoning is that with an interactive selection, you can make sure that you avoid screenshotting sensitive content.\n\n```kdl\nwindow-rule {\n    block-out-from \"screen-capture\"\n}\n```\n\n> [!WARNING]\n> Be careful when blocking out windows based on a dynamically changing window title.\n>\n> For example, you might try to block out specific Firefox tabs like this:\n>\n> ```kdl\n> window-rule {\n>     // Doesn't quite work! Try to block out the Gmail tab.\n>     match app-id=\"firefox$\" title=\"- Gmail \"\n>\n>     block-out-from \"screencast\"\n> }\n> ```\n>\n> It will work, but when switching from a sensitive tab to a regular tab, the contents of the sensitive tab **will show up on a screencast** for an instant.\n>\n> This is because window title (and app ID) are not double-buffered in the Wayland protocol, so they are not tied to specific window contents.\n> There's no robust way for Firefox to synchronize visibly showing a different tab and changing the window title.\n\n#### `opacity`\n\nSet the opacity of the window.\n`0.0` is fully transparent, `1.0` is fully opaque.\nThis is applied on top of the window's own opacity, so semitransparent windows will become even more transparent.\n\nOpacity is applied to every surface of the window individually, so subsurfaces and pop-up menus will show window content behind them.\n\n![Screenshot showing Adwaita Demo with a semitransparent pop-up menu.](./img/opacity-popup.png)\n\nAlso, focus ring and border with background will show through semitransparent windows (see `prefer-no-csd` and the `draw-border-with-background` window rule below).\n\nOpacity can be toggled on or off for a window using the [`toggle-window-rule-opacity`](./Configuration:-Key-Bindings.md#toggle-window-rule-opacity) action.\n\n```kdl\n// Make inactive windows semitransparent.\nwindow-rule {\n    match is-active=false\n\n    opacity 0.95\n}\n```\n\n#### `variable-refresh-rate`\n\n<sup>Since: 0.1.9</sup>\n\nIf set to true, whenever this window displays on an output with on-demand VRR, it will enable VRR on that output.\n\n```kdl\n// Configure some output with on-demand VRR.\noutput \"HDMI-A-1\" {\n    variable-refresh-rate on-demand=true\n}\n\n// Enable on-demand VRR when mpv displays on the output.\nwindow-rule {\n    match app-id=\"^mpv$\"\n\n    variable-refresh-rate true\n}\n```\n\n#### `default-column-display`\n\n<sup>Since: 25.02</sup>\n\nSet the default display mode for columns created from this window.\nCan be `normal` or `tabbed`.\n\nThis is used any time a window goes into its own column.\nFor example:\n- Opening a new window.\n- Expelling a window into its own column.\n- Moving a window from the floating layout to the tiling layout.\n\n```kdl\n// Make Evince windows open as tabbed columns.\nwindow-rule {\n    match app-id=\"^evince$\"\n\n    default-column-display \"tabbed\"\n}\n```\n\n#### `default-floating-position`\n\n<sup>Since: 25.01</sup>\n\nSet the initial position for this window when it opens on, or moves to the floating layout.\n\nAfterward, the window will remember its last floating position.\n\nBy default, new floating windows open at the center of the screen, and windows from the tiling layout open close to their visual screen position.\n\nThe position uses logical coordinates relative to the working area.\nBy default, they are relative to the top-left corner of the working area, but you can change this by setting `relative-to` to one of these values: `top-left`, `top-right`, `bottom-left`, `bottom-right`, `top`, `bottom`, `left`, or `right`.\n\nFor example, if you have a bar at the top, then `x=0 y=0` will put the top-left corner of the window directly below the bar.\nIf instead you write `x=0 y=0 relative-to=\"top-right\"`, then the top-right corner of the window will align with the top-right corner of the workspace, also directly below the bar.\nWhen only one side is specified (e.g. top) the window will align to the center of that side.\n\nThe coordinates change direction based on `relative-to`.\nFor example, by default (top-left), `x=100 y=200` will put the window 100 pixels to the right and 200 pixels down from the top-left corner.\nIf you use `x=100 y=200 relative-to=\"bottom-left\"`, it will put the window 100 pixels to the right and 200 pixels *up* from the bottom-left corner.\n\n```kdl\n// Open the Firefox picture-in-picture window at the bottom-left corner of the screen\n// with a small gap.\nwindow-rule {\n    match app-id=\"firefox$\" title=\"^Picture-in-Picture$\"\n\n    default-floating-position x=32 y=32 relative-to=\"bottom-left\"\n}\n```\n\nYou can use single-side `relative-to` to get a dropdown-like effect.\n\n```kdl\n// Example: a \"dropdown\" terminal.\nwindow-rule {\n    // Match by \"dropdown\" app ID.\n    // You need to set this app ID when running your terminal, e.g.:\n    // spawn \"alacritty\" \"--class\" \"dropdown\"\n    match app-id=\"^dropdown$\"\n\n    // Open it as floating.\n    open-floating true\n    // Anchor to the top edge of the screen.\n    default-floating-position x=0 y=0 relative-to=\"top\"\n    // Half of the screen high.\n    default-window-height { proportion 0.5; }\n    // 80% of the screen wide.\n    default-column-width { proportion 0.8; }\n}\n```\n\n#### `scroll-factor`\n\n<sup>Since: 25.02</sup>\n\nSet a scroll factor for all scroll events sent to a window.\n\nThis will be multiplied with the scroll factor set for your input device in the [input section](./Configuration:-Input.md#pointing-devices).\n\n```kdl\n// Make scrolling in Firefox a bit slower.\nwindow-rule {\n    match app-id=\"firefox$\"\n\n    scroll-factor 0.75\n}\n```\n\n#### `draw-border-with-background`\n\nOverride whether the border and the focus ring draw with a background.\n\nSet this to `true` to draw them as solid colored rectangles even for windows which agreed to omit their client-side decorations.\nSet this to `false` to draw them as borders around the window even for windows which use client-side decorations.\n\nThis property can be useful for rectangular windows that do not support the xdg-decoration protocol.\n\n| With Background                                  | Without Background                                  |\n| ------------------------------------------------ | --------------------------------------------------- |\n| ![A screenshot displaying a window with draw-border-with-background set to true](./img/simple-egl-border-with-background.png) | ![A screenshot displaying a window with draw-border-with-background set to false](./img/simple-egl-border-without-background.png) |\n\n```kdl\nwindow-rule {\n    draw-border-with-background false\n}\n```\n\n#### `focus-ring` and `border`\n\n<sup>Since: 0.1.6</sup>\n\nOverride the focus ring and border options for the window.\n\nThese rules have the same options as the normal [`focus-ring` and `border` config in the layout section](./Configuration:-Layout.md#focus-ring-and-border), so check the documentation there.\n\nHowever, in addition to `off` to disable the border/focus ring, this window rule has an `on` flag that enables the border/focus ring for the window even if it was otherwise disabled.\nThe `on` flag has precedence over the `off` flag, in case both are set.\n\n```kdl\nwindow-rule {\n    focus-ring {\n        off\n        width 2\n    }\n}\n\nwindow-rule {\n    border {\n        on\n        width 8\n    }\n}\n```\n\n#### `shadow`\n\n<sup>Since: 25.02</sup>\n\nOverride the shadow options for the window.\n\nThis rule has the same options as the normal [`shadow` config in the layout section](./Configuration:-Layout.md#shadow), so check the documentation there.\n\nHowever, in addition to `on` to enable the shadow, this window rule has an `off` flag that disables the shadow for the window even if it was otherwise enabled.\nThe `on` flag has precedence over the `off` flag, in case both are set.\n\n```kdl\n// Turn on shadows for floating windows.\nwindow-rule {\n    match is-floating=true\n\n    shadow {\n        on\n    }\n}\n```\n\n#### `tab-indicator`\n\n<sup>Since: 25.02</sup>\n\nOverride the tab indicator options for the window.\n\nOptions in this rule match the same options as the normal [`tab-indicator` config in the layout section](./Configuration:-Layout.md#tab-indicator), so check the documentation there.\n\n```kdl\n// Make KeePassXC tab have a dark red inactive color.\nwindow-rule {\n    match app-id=r#\"^org\\.keepassxc\\.KeePassXC$\"#\n\n    tab-indicator {\n        inactive-color \"darkred\"\n    }\n}\n```\n\n#### `geometry-corner-radius`\n\n<sup>Since: 0.1.6</sup>\n\nSet the corner radius of the window.\n\nOn its own, this setting will only affect the border and the focus ring—they will round their corners to match the geometry corner radius.\nIf you'd like to force-round the corners of the window itself, set [`clip-to-geometry true`](#clip-to-geometry) in addition to this setting.\n\n```kdl\nwindow-rule {\n    geometry-corner-radius 12\n}\n```\n\nThe radius is set in logical pixels, and controls the radius of the window itself, that is, the inner radius of the border:\n\n![A screenshot showing a window with every corner rounded](./img/geometry-corner-radius.png)\n\nInstead of one radius, you can set four, for each corner.\nThe order is the same as in CSS: top-left, top-right, bottom-right, bottom-left.\n\n```kdl\nwindow-rule {\n    geometry-corner-radius 8 8 0 0\n}\n```\n\nThis way, you can match GTK 3 applications which have square bottom corners:\n\n![A screenshot showing a window with only the top corners rounded](./img/different-corner-radius.png)\n\n#### `clip-to-geometry`\n\n<sup>Since: 0.1.6</sup>\n\nClips the window to its visual geometry.\n\nThis will cut out any client-side window shadows, and also round window corners according to `geometry-corner-radius`.\n\n![A screenshot showing a window with rounded corners, clipped to the visual geometry](./img/clip-to-geometry.png)\n\n```kdl\nwindow-rule {\n    clip-to-geometry true\n}\n```\n\nEnable border, set [`geometry-corner-radius`](#geometry-corner-radius) and `clip-to-geometry`, and you've got a classic setup:\n\n![A screenshot showing a window with rounded corners, and a border](./img/border-radius-clip.png)\n\n```kdl\nprefer-no-csd\n\nlayout {\n    focus-ring {\n        off\n    }\n\n    border {\n        width 2\n    }\n}\n\nwindow-rule {\n    geometry-corner-radius 12\n    clip-to-geometry true\n}\n```\n\n#### `tiled-state`\n\n<sup>Since: 25.05</sup>\n\nInforms the window that it is tiled.\nUsually, windows will react by becoming rectangular and hiding their client-side shadows.\nWindows that snap their size to a grid (e.g. terminals like [foot](https://codeberg.org/dnkl/foot)) will usually disable this snapping when they are tiled.\n\nBy default, niri will set the tiled state to `true` together with [`prefer-no-csd`](./Configuration:-Miscellaneous.md#prefer-no-csd) in order to improve behavior for apps that don't support server-side decorations.\nYou can use this window rule to override this, for example to get rectangular windows with CSD.\n\n```kdl\n// Make tiled windows rectangular while using CSD.\nwindow-rule {\n    match is-floating=false\n\n    tiled-state true\n}\n```\n\n#### `baba-is-float`\n\n<sup>Since: 25.02</sup>\n\nMake your windows FLOAT up and down.\n\nThis is an April Fools' 2025 feature.\n\n```kdl\nwindow-rule {\n    match is-floating=true\n\n    baba-is-float true\n}\n```\n\n<video controls src=\"https://github.com/user-attachments/assets/3f4cb1a4-40b2-4766-98b7-eec014c19509\">\n\nhttps://github.com/user-attachments/assets/3f4cb1a4-40b2-4766-98b7-eec014c19509\n\n</video>\n\n#### Size Overrides\n\nYou can amend the window's minimum and maximum size in logical pixels.\n\nKeep in mind that the window itself always has a final say in its size.\nThese values instruct niri to never ask the window to be smaller than the minimum you set, or to be bigger than the maximum you set.\n\n> [!NOTE]\n> `max-height` will only apply to automatically-sized windows if it is equal to `min-height`.\n> Either set it equal to `min-height`, or change the window height manually after opening it with `set-window-height`.\n>\n> This is a limitation of niri's window height distribution algorithm.\n\n```kdl\nwindow-rule {\n    min-width 100\n    max-width 200\n    min-height 300\n    max-height 300\n}\n```\n\n```kdl\n// Fix OBS with server-side decorations missing a minimum width.\nwindow-rule {\n    match app-id=r#\"^com\\.obsproject\\.Studio$\"#\n\n    min-width 876\n}\n```\n"
  },
  {
    "path": "docs/wiki/Development:-Animation-Timing.md",
    "content": "> *Time, Dr. Freeman? Is it really that... time again?*\n\nA compositor deals with one or more monitors on mostly fixed refresh cycles.\nFor example, a 170 Hz monitor can draw a frame every ~5.88 ms.\n\nMost of the time, the compositor doesn't actually redraw the monitor: when nothing changes on screen (e.g. you're reading a document and aren't moving your cursor), it would be wasteful to wake up the GPU to composite the same image.\nDuring an animation however, screen contents do change every frame.\nNiri will generally start drawing the next frame as soon as the previous one shows up on screen.\n\nSince the monitor refresh cycle is fixed in most cases (even with VRR, there's a maximum refresh rate), the compositor can predict when the next frame will show up on the monitor, and render ongoing animations for that exact moment in time.\nThis way, all animation frames are perfectly timed with no jitter, regardless of when exactly the rendering code had a chance to run.\nFor example, even if the compositor has to process new window events, delaying the rendering by a few ms, the animation timing will remain exactly aligned to the monitor refresh cycle.\n\nThere are hence several properties that a compositor wants from its timing system.\n\n1. It should be possible to get the state of the animations at a specific time in the near future, for rendering a frame exactly timed to when the monitor will show it.\n    - This time override ability should be usable in tests to advance the time in a fully controlled fashion.\n1. Animations in response to user actions should begin at the moment when the action happens.\n   For example, pressing a workspace switch key should start the animation at the instant when the user pressed the key (rather than, say, slightly in the future where we predicted the next monitor frame, which we had already rendered by now).\n1. During the processing of a single action, querying the current time should return the exact same value.\n   Even if the processing finishes a few microseconds after it started, querying the time in the end should return the same thing.\n   This generally makes writing code much more sane; otherwise you'd need to for example avoid reading the position of some element twice in a row, since it could have moved by one pixel in-between, screwing with the logic.\n   Also, fetching the current system time [can be quite expensive](https://mastodon.online/@YaLTeR/109934977035721850) in terms of overhead.\n1. It should be reasonably easy to implement an animation slow-down preference, so all animations can be slowed down or sped up by the same factor.\n\nThe solution in niri is a `LazyClock`, a clock that remembers one timestamp.\nInitially, the timestamp is empty, so when you ask `LazyClock` for the current time, it will fetch and return the system time, and also remember it.\nSubsequently, it will keep returning the same timestamp that it had remembered.\n\nYou can also clear the timestamp, then `LazyClock` will fetch the system time anew when it's needed.\nIn niri, the timestamp is cleared at the end of every event loop iteration, right before going to sleep waiting for new events.\nThis way, anything that happens next (like a user key press) will fetch and use the most up-to-date timestamp as soon as one is needed, but then the processing code will keep getting the exact same timestamp, since `LazyClock` stores it.\n\nYou can also just manually set the timestamp to a specific value.\nThis is how we render a frame for the predicted time of when the monitor will show it.\nAlso, this is used by tests: they simply always set the timestamp and never use the system time.\n\nFinally, there's an `AdjustableClock` wrapper on top that provides the ability to control the slow-down rate by modifying the timestamps returned by the clock.\n\nAn important detail is that with rate changes, timestamps from the `AdjustableClock` will drift away and become unrelated to the system time.\nHowever, our target timestamp (for rendering) comes from the system time, so the override works directly on the underlying `LazyClock`.\nThat is, overriding the timestamp and then querying the `AdjustableClock` will return a *different* timestamp that is correct and consistent with the adjustments made by `AdjustableClock`.\nThis is reflected in the API by naming the function `Clock::set_unadjusted()` (and there's also `Clock::now_unadjusted()` to get the raw timestamp).\n\nThe clock is shared among all animations in niri through passing around and storing a reference-counted pointer.\nThis way, overriding the time automatically applies to everything, whereas in tests we can use a separate clock per test so that they don't interfere with each other.\n"
  },
  {
    "path": "docs/wiki/Development:-Design-Principles.md",
    "content": "## General principles\n\nThese are some of the general principles that I try to follow throughout niri.\nThey can be sidestepped in specific circumstances if there's a good reason.\n\n### Opening a new window should not affect the sizes of any existing windows.\n\nThis is the main annoyance with traditional tiling: you want to open a new window, but it messes with your existing window sizes.\nEspecially when you're looking at a big window like a browser or an image editor, want to open a quick terminal for something, and it makes the big window unusably small, or reflows the content, or clips part of the window.\n\nThe usual workaround in tiling WMs is to use more workspaces: when you need a new window, you go to an empty workspace and open it there (this way, you also get your entire screen for the new window, rather than a smaller part of it).\n\nScrollable tiling offers an alternative: for temporary windows, you can just open them, do what you need, and close, all without messing up the other windows or having to go to a new workspace.\nIt also lets you group together more related windows on the same workspace by having less frequently used ones scrolled out of the view.\n\n### The focused window should not move around on its own.\n\nIn particular: windows opening, closing, and resizing to the left of the focused window should not cause it to visually move.\n\nThe focused window is the window you're working in.\nAnd stuff happening outside the view shouldn't mess with what you're focused on.\n\n### Actions should apply immediately.\n\nThis is important both for compositor responsiveness and predictability, and for keeping the code sane and free of edge cases and unnecessary asynchrony.\n\n- Things like resizing or consuming into column take effect immediately, even if the window needs time to catch up.\n- An animated workspace switch makes your input go to the final workspace and window instantly, without waiting for the animation.\n- Opening the overview (which has a zoom-out animation) lets you grab windows right away, and closing the overview makes your input immediately go back to the windows, without waiting for the zoom back in.\n\n### When disabled, eye-candy features should not affect the performance.\n\nThings like animations and custom shaders do not run and are not present in the render tree when disabled.\nExtra offscreen rendering is avoided.\n\nAnimations specifically are still \"started\" even when disabled, but with a duration of 0 (this way, they end as soon as the time is advanced).\nThis does not impact performance, but helps avoid a lot of edge cases in the code.\n\n### Eye-candy features should not cause unreasonable excessive rendering.\n\n- For example, clip-to-geometry will prevent direct scanout in many cases (since the window surface is not completely visible). But in the cases where the surface or the subsurface *is* completely visible (fully within the clipped region), it will still allow for direct scanout.\n- For example, animations *can* cause damage and even draw to an offscreen every frame, because they are expected to be short (and can be disabled). However, something like the rounded corners shader should not offscreen or cause excessive damage every frame, because it is long-running and constantly active.\n\n### Be mindful of invisible state.\n\nThis is niri state that is not immediately apparent from looking at the screen. This is not bad per se, but you should carefully consider how to reduce the surprise factor.\n\n- For example, when a monitor disconnects, all its workspaces move to another connected monitor. In order to be able to restore these workspaces when the first monitor connects again, these workspaces keep the knowledge of which was their *original monitor*—this is an example of invisible state, since you can't tell it in any way by looking at the screen. This can have surprising consequences: imagine disconnecting a monitor at home, going to work, completely rearranging the windows there, then coming back home, and suddenly some random workspaces end up on your home monitor. In order to reduce this surprise factor, whenever a new window appears on a workspace, that workspace resets its *original monitor* to its current monitor. This way, the workspaces you actively worked on remain where they were.\n- For example, niri preserves the view position whenever a window appears, or whenever a window goes full-screen, to restore it afterward. This way, dealing with temporary things like dialogs opening and closing, or toggling full-screen, becomes less annoying, since it doesn't mess up the view position. This is also invisible state, as you cannot tell by looking at the screen where closing a window will restore the view position. If taken to the extreme (previous view position saved forever for every open window), this can be surprising, as closing long-running windows would result in the view shifting around pretty much randomly. To reduce this surprise factor, niri remembers only one last view position per workspace, and forgets this stored view position upon window focus change.\n\n## Window layout\n\nHere are some design considerations for the window layout logic.\n\n1. If a window or popup is larger than the screen, it should be aligned in the top left corner.\n\n    The top left area of a window is more likely to contain something important, so it should always be visible.\n\n1. Setting window width or height to a fixed pixel size (e.g. `set-column-width 1280` or `default-column-width { fixed 1280; }`) will set the size of the window itself, however setting to a proportional size (e.g. `set-column-width 50%`) will set the size of the tile, including the border added by niri.\n\n    - With proportions, the user is looking to tile multiple windows on the screen, so they should include borders.\n    - With fixed sizes, the user wants to test a specific client size or take a specifically sized screenshot, so they should affect the window directly.\n    - After the size is set, it is always converted to a value that includes the borders, to make the code sane. That is, `set-column-width 1000` followed by changing the niri border width will resize the window accordingly.\n\n1. Fullscreen windows are a normal part of the scrolling layout.\n\n    This is a cool idea that scrollable tiling is uniquely positioned to implement.\n    Fullscreen windows aren't on some \"special\" layer that covers everything; instead, they are normal tiles that you can switch away from, without disturbing the fullscreen status.\n\n    Of course, you do want to cover your entire monitor when focused on a fullscreen window.\n    This is specifically hardcoded into the logic: when the view is stationary on a focused fullscreen window, the top layer-shell layer and the floating windows hide away.\n\n    This is also why fullscreening a floating window makes it go into the scrolling layout.\n\n## Default config\n\nThe [default config](https://github.com/niri-wm/niri/blob/main/resources/default-config.kdl) is intended to give a familiar, helpful, and not too jarring experience to new niri users.\nImportantly, it is not a \"suggested rice config\"; we don't want to startle people with full-on rainbow borders and crazy shaders.\n\nSince we're not a complete desktop environment (and don't have the contributor base to become one), we cannot provide a fully integrated experience—distro spins are better positioned to do this.\nAs such, new niri users are expected to read through and tinker with the default niri config.\n\nThe default config is therefore thoroughly commented with links to the relevant wiki sections.\nWe don't include every possible option in the default config to avoid overwhelming users too much; anything overly specific or uncommon can stay on the wiki.\nThe general rule is to include things that users are reasonably expected to want to change or know how to do.\nWe do also advertise our more unique features though like screencast block-out-from.\n\nWe default to CSD (`prefer-no-csd` is commented out).\nThis gives new users easy and familiar way to move and close windows via their titlebars, especially considering that niri doesn't have serverside titlebars (so far at least).\n\nFocus rings are drawn fully behind windows by default.\nWhile this unfortunately messes with window transparency, [which is a common source of confusion](./FAQ.md#why-are-transparent-windows-tinted-why-is-the-borderfocus-ring-showing-up-through-semitransparent-windows), defaulting to drawing focus rings only around windows would be even worse because it has holes inside clientside rounded corners.\nThe ideal solution here would be to propose a Wayland protocol for windows to report their corner radius to the compositor (which would generally help for serverside decorations in different compositors).\n\nThe default focus ring is quite thick at 4 px to look well with clientside-decorated windows and be obviously noticeable, and the default gaps are also quite big at 16 px to look well with the default focus ring width.\n\nThe default input settings like touchpad tap and natural-scroll are how I believe most people want to use their computers.\n\nShadows default to off because they are a fairly performance-intensive shader, and because many clientside-decorated windows already draw their own shadows.\n\nThe default screenshot-path matches GNOME Shell.\n\nDefault window rules are limited to fixing known severe issues (WezTerm) and doing something the absolute majority likely wants (make Firefox Picture-in-Picture player floating—it can't do that on its own currently, maybe the pip protocol will change that).\n\nThe default binds largely come from my own experience using PaperWM, and from other compositors.\nThey assume QWERTY.\nThe binds are ordered in a way to gradually introduce you to different bind configuration concepts.\n\nThe general system is: if a hotkey switches somewhere, then adding <kbd>Ctrl</kbd> will move the focused window or column there.\nAdding <kbd>Shift</kbd> does an alternative action: for focus and movement it starts going across monitors, for resizes it starts acting on window height rather than width, etc.\nWorkspace switching on <kbd>Mod</kbd><kbd>U</kbd>/<kbd>I</kbd> is one key up from <kbd>Mod</kbd><kbd>J</kbd>/<kbd>K</kbd> used for window switching.\n\nSince <kbd>Alt</kbd> is a modifier in nested niri, binds with explicit <kbd>Alt</kbd> are mainly the ones only useful on the host, for example spawning a screen locker.\n"
  },
  {
    "path": "docs/wiki/Development:-Developing-niri.md",
    "content": "## Running a Local Build\n\nThe main way of testing niri during development is running it as a nested window. The second step is usually switching to a different TTY and running niri there.\n\nOnce a feature or fix is reasonably complete, you generally want to run a local build as your main compositor for proper testing. The easiest way to do that is to install niri normally (from a distro package for example), then overwrite the binary with `sudo cp ./target/release/niri /usr/bin/niri`. Do make sure that you know how to revert to a working version in case everything breaks though.\n\nIf you use an RPM-based distro, you can generate an RPM package for a local build with `cargo generate-rpm`.\n\n## Logging Levels\n\nNiri uses [`tracing`](https://lib.rs/crates/tracing) for logging. This is how logging levels are used:\n\n- `error!`: programming errors and bugs that are recoverable. Things you'd normally use `unwrap()` for. However, when a Wayland compositor crashes, it brings down the entire session, so it's better to recover and log an `error!` whenever reasonable. If you see an `ERROR` in the niri log, that always indicates a *bug*.\n- `warn!`: something bad but still *possible* happened. Informing the user that they did something wrong, or that their hardware did something weird, falls into this category. For example, config parsing errors should be indicated with a `warn!`.\n- `info!`: the most important messages related to normal operation. Running niri with `RUST_LOG=niri=info` should not make the user want to disable logging altogether.\n- `debug!`: less important messages related to normal operation. Running niri with `debug!` messages hidden should not negatively impact the UX.\n- `trace!`: everything that can be useful for debugging but is otherwise too spammy or performance intensive. `trace!` messages are *compiled out* of release builds.\n\n## Tests\n\nWe have some unit tests, most prominently for the layout code and for config parsing.\n\nWhen adding new operations to the layout, add them to the `Op` enum at the bottom of `src/layout/mod.rs` (this will automatically include it in the randomized tests), and if applicable to the `every_op` arrays below.\n\nWhen adding new config options, include them in the config parsing test.\n\n### Running Tests\n\nMake sure to run `cargo test --all` to run tests from sub-crates too.\n\nSome tests are a bit too slow to run normally, like the randomized tests of the layout code, so they are normally skipped. Set the `RUN_SLOW_TESTS` variable to run them:\n\n```\nenv RUN_SLOW_TESTS=1 cargo test --all\n```\n\nIt also usually helps to run the randomized tests for a longer period, so that they can explore more inputs. You can control this with environment variables. This is how I usually run tests before pushing:\n\n```\nenv RUN_SLOW_TESTS=1 PROPTEST_CASES=200000 PROPTEST_MAX_GLOBAL_REJECTS=200000 RUST_BACKTRACE=1 cargo test --release --all\n```\n\n### Visual Tests\n\nThe `niri-visual-tests` sub-crate is a GTK application that runs hard-coded test cases so that you can visually check that they look right. It uses mock windows with the real layout and rendering code. It is especially helpful when working on animations.\n\n## Profiling\n\nWe have integration with the [Tracy](https://github.com/wolfpld/tracy) profiler which you can enable by building niri with a feature flag:\n\n```\ncargo build --release --features=profile-with-tracy-ondemand\n```\n\nThen you can open Tracy (you will need the latest stable release) and attach to a running niri instance to collect profiling data. Profiling data is collected \"on demand\"—that is, only when Tracy is connected. You can run a niri build like this as your main compositor if you'd like.\n\n> [!NOTE]\n> If you need to profile niri startup or the niri CLI, you can opt for \"always on\" profiling instead, using this feature flag:\n>\n> ```\n> cargo build --release --features=profile-with-tracy\n> ```\n>\n> When compiled this way, niri will **always** collect profiling data, so you can't run a build like this as your main compositor.\n\nTo make a niri function show up in Tracy, instrument it like this:\n\n```rust\npub fn some_function() {\n    let _span = tracy_client::span!(\"some_function\");\n\n    // Code of the function.\n}\n```\n\nYou can also enable Rust memory allocation profiling with `--features=profile-with-tracy-allocations`.\n"
  },
  {
    "path": "docs/wiki/Development:-Documenting-niri.md",
    "content": "niri's documentation files are found in `docs/wiki/` and should be viewable and browsable in at least three systems:\n\n- The GitHub repo's markdown file preview\n- [The GitHub repo's wiki](https://github.com/niri-wm/niri/wiki)\n- [The documentation site](https://niri-wm.github.io/niri/)\n\n## The GitHub repo's wiki\n\nThis is generated with the `publish-wiki` job in `.github/workflows/ci.yml`.\nIn 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.\nThis could be useful as a contributor to verify that the wiki generates the way you expect it to.\n\n## The documentation site\n\nThe documentation site is generated with [mkdocs](https://www.mkdocs.org/).\nThe configuration files are found in `docs/`.\n\nTo set up and run the documentation site locally, it is recommended to use [uv](https://docs.astral.sh/uv/).\n\n### Serving the site locally with uv\n\nIn the `docs/` subdirectory:\n\n- `uv sync`\n- `uv run mkdocs serve`\n\nThe documentation site should now be available on http://127.0.0.1:8000/niri/\n\nChanges made to the documentation while the development server is running will cause an automatic page refresh in the browser.\n\n> [!TIP]\n> Images may not be visible, as they are stored on Git LFS.\n> If this is the case, run `git lfs pull`.\n\n## Elements\n\nElements 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.\n\n### Links\n\nLinks should in all cases be relative (e.g. `./FAQ.md`), unless it's an external one.\nLinks should have anchors if they are meant to lead the user to a specific section on a page (e.g. `./Getting-Started.md#nvidia`).\n\n> [!TIP]\n> mkdocs will terminate if relative links lead to non-existing documents or non-existing anchors.\n> This means that the CI pipeline will fail when building documentation, as will `mkdocs serve` locally.\n\n### Admonitions\n\n> [!IMPORTANT]\n> This is an important distinction from other `mkdocs`-based documentation you might have encountered.\n\nAdmonitions, 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).\nThe above admonition is written like this:\n\n```\n> [!IMPORTANT]\n> This is an important distinction from other `mkdocs`-based documentation you might have encountered.\n```\n\n### Images\n\nImages should have relative links to resources in `docs/wiki/img/`, and should contain sensible alt-text.\n\n### Videos\n\nFor 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.\n\n```html\n<video controls src=\"https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995\">\n\nhttps://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995\n\n</video>\n```\n\n### Snippets\n\nConfiguration and code snippets in general should be annotated with a language.\n\nIf the language used in the snippet is KDL, open the code block like this:\n\n```md\n```kdl\n```\n"
  },
  {
    "path": "docs/wiki/Development:-Fractional-Layout.md",
    "content": "There are two main coordinate spaces in niri: physical (pixels of every individual output) and logical (shared among all outputs, takes into account the scale of every output).\nWayland clients mostly work in the logical space, and it's the most convenient space to do all the layout in, since it bakes in the output scaling factor.\n\nHowever, many things need to be sized or positioned at integer physical coordinates.\nFor example, Wayland toplevel buffers are assumed to be placed at an integer physical pixel on an output (and `WaylandSurfaceRenderElement` will do that for you).\nBorders and focus rings should also have a width equal to an integer number of physical pixels to stay crisp (not to mention that `SolidColorRenderElement` does not anti-alias lines at fractional pixel positions).\n\nInteger physical coordinates do not necessarily correspond to integer logical coordinates though.\nEven with an integer scale = 2, a physical pixel at (1, 1) will be at the logical position of (0.5, 0.5).\nThis problem becomes much worse with fractional scale factors where most integer logical coordinates will fall on fractional physical coordinates.\n\nThus, niri uses fractional logical coordinates for most of its layout.\nHowever, one needs to be very careful to keep things aligned to the physical grid to avoid artifacts like:\n\n* Border width alternating 1 px thicker/thinner\n* Border showing 1 px off from the window at certain positions\n* 1 px gaps around rounded corners\n* Slightly blurry window contents during resizes\n* And so on...\n\nThe way it's handled in niri is:\n\n1. All relevant sizes on a workspace are rounded to an integer physical coordinate according to the current output scale. Things like struts, gaps, border widths, working area location.\n\n    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.\n    The rounding looks something like: `(logical_size * scale).round() / scale`.\n    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.\n\n2. The view offset and individual column/tile render offsets are *not* rounded to physical pixels, but:\n3. `tiles_with_render_positions()` rounds tile positions to physical pixels as it returns them,\n4. Custom shaders like opening, closing and resizing windows, are also careful to keep positions and sizes rounded to the physical pixels.\n\nThe idea is that every tile can assume that it is rendered at an integer physical coordinate, therefore when shifting the position by, say, border width (also rounded to integer physical coordinates), the new position will stay rounded to integer physical coordinates.\nThe same logic works for the rest of the layout thanks to gaps, struts and working area being similarly rounded.\nThis way, the entire layout is always aligned, as long as it is positioned at an integer physical coordinate (which rounding the tile positions effectively achieves).\n"
  },
  {
    "path": "docs/wiki/Development:-Redraw-Loop.md",
    "content": "On a TTY, only one frame can be submitted to an output at a time, and the compositor must wait until the output repaints (indicated by a VBlank) to be able to submit the next frame.\nIn niri we keep track of this via the `RedrawState` enum that you can find in an `OutputState`.\n\nHere's a diagram of state transitions for the `RedrawState` state machine:\n\n<picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"./img/RedrawState-dark.drawio.png\">\n    <img alt=\"RedrawState state transition diagram\" src=\"./img/RedrawState-light.drawio.png\">\n</picture>\n\n`Idle` is the default state, when the output does not need to be repainted.\nAny operation that may cause the screen to update calls `queue_redraw()`, which moves the output to a `Queued` state.\nThen, at the end of an event loop dispatch, niri calls `redraw()` for every `Queued` output.\n\nIf the redraw causes damage (i.e. something on the output changed), we move into the `WaitingForVBlank` state, since we cannot redraw until we receive a VBlank event.\nHowever, if there's no damage, we do not return to `Idle` right away.\nInstead, we set a timer to fire roughly at when the next VBlank would occur, and transition to a `WaitingForEstimatedVBlank` state.\n\nThis is necessary in order to throttle frame callbacks sent to applications to at most once per output refresh cycle.\nWithout this throttling, applications can start continuously redrawing without damage (for instance, if the application window is partially off-screen, and it is only the off-screen part that changes), and eating a lot of CPU in the process.\n\nThen, either the estimated VBlank timer completes, and we go back to `Idle`, or maybe we call `queue_redraw()` once more and try to redraw again.\n"
  },
  {
    "path": "docs/wiki/Example-systemd-Setup.md",
    "content": "When starting niri from a display manager like GDM, or otherwise through the `niri-session` binary, it runs as a systemd service.\nThis provides the necessary systemd integration to run programs like `mako` and services like `xdg-desktop-portal` bound to the graphical session.\n\nHere'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.\nUnlike [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup), this lets you easily monitor their status and output, and restart or reload them.\n\n1. Install them, i.e. `sudo dnf install mako waybar swaybg swayidle`\n2. `mako` and `waybar` provide systemd units out of the box, so you can simply add them to the niri session:\n\n    ```\n    systemctl --user add-wants niri.service mako.service\n    systemctl --user add-wants niri.service waybar.service\n    ```\n\n    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`.\n\n3. `swaybg` does not provide a systemd unit, since you need to pass the background image as a command-line argument.\n    So we will make our own.\n    Create `~/.config/systemd/user/swaybg.service` with the following contents:\n\n    ```systemd\n    [Unit]\n    PartOf=graphical-session.target\n    After=graphical-session.target\n    Requisite=graphical-session.target\n\n    [Service]\n    ExecStart=/usr/bin/swaybg -m fill -i \"%h/Pictures/LakeSide.png\"\n    Restart=on-failure\n    ```\n\n    Replace the image path with the one you want.\n    `%h` is expanded to your home directory.\n\n    After editing `swaybg.service`, run `systemctl --user daemon-reload` so systemd picks up the changes in the file.\n\n    Now, add it to the niri session:\n\n    ```\n    systemctl --user add-wants niri.service swaybg.service\n    ```\n\n4. `swayidle` similarly does not provide a service, so we will also make our own.\n    Create `~/.config/systemd/user/swayidle.service` with the following contents:\n\n    ```systemd\n    [Unit]\n    PartOf=graphical-session.target\n    After=graphical-session.target\n    Requisite=graphical-session.target\n\n    [Service]\n    ExecStart=/usr/bin/swayidle -w timeout 601 'niri msg action power-off-monitors' timeout 600 'swaylock -f' before-sleep 'swaylock -f'\n    Restart=on-failure\n    ```\n\n    Then, run `systemctl --user daemon-reload` and add it to the niri session:\n\n    ```\n    systemctl --user add-wants niri.service swayidle.service\n    ```\n\nThat's it!\nNow these three utilities will be started together with the niri session and stopped when it exits.\nYou can also restart them with a command like `systemctl --user restart waybar.service`, for example after editing their config files.\n\nTo remove a service from niri startup, remove its symbolic link from `~/.config/systemd/user/niri.service.wants/`.\nThen, run `systemctl --user daemon-reload`.\n\n### Running Programs Across Logout\n\nWhen 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:\n\n```\nsystemd-run --user --scope tmux new-session\n```\n"
  },
  {
    "path": "docs/wiki/FAQ.md",
    "content": "### How to disable client-side decorations/make windows rectangular?\n\nUncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config, and then restart your apps.\nThen 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).\n\nNote that currently this will prevent edge window resize handles from showing up.\nYou can still resize windows by holding <kbd>Mod</kbd> and the right mouse button.\n\n### Why are transparent windows tinted? / Why is the border/focus ring showing up through semitransparent windows?\n\nUncomment the [`prefer-no-csd` setting](./Configuration:-Miscellaneous.md#prefer-no-csd) at the top level of the config, and then restart your apps.\nNiri will draw focus rings and borders *around* windows that agree to omit their client-side decorations.\n\nBy default, focus ring and border are rendered as a solid background rectangle behind windows.\nThat is, they will show up through semitransparent windows.\nThis is because windows using client-side decorations can have an arbitrary shape.\n\nYou can also override this behavior with the [`draw-border-with-background` window rule](./Configuration:-Window-Rules.md#draw-border-with-background).\n\n### How to enable rounded corners for all windows?\n\nPut this window rule in your config:\n\n```kdl\nwindow-rule {\n    geometry-corner-radius 12\n    clip-to-geometry true\n}\n```\n\nFor more information, check the [`geometry-corner-radius` window rule](./Configuration:-Window-Rules.md#geometry-corner-radius).\n\n### How to hide the \"Important Hotkeys\" pop-up at the start?\n\nPut this into your config:\n\n```kdl\nhotkey-overlay {\n    skip-at-startup\n}\n```\n\n### How to run X11 apps like Steam or Discord?\n\nTo run X11 apps, you can use [xwayland-satellite](https://github.com/Supreeeme/xwayland-satellite).\nCheck [the Xwayland wiki page](./Xwayland.md) for instructions.\n\nKeep in mind that you can run many Electron apps such as VSCode or Discord natively on Wayland by passing the right flags, as described [here](./Application-Issues.md#electron-applications).\n\n### Why doesn't niri integrate Xwayland like other compositors?\n\nA combination of factors:\n\n- Integrating Xwayland is quite a bit of work, as the compositor needs to implement parts of an X11 window manager.\n- You need to appease the X11 ideas of windowing, whereas for niri I want to have the best code for Wayland.\n- niri doesn't have a good global coordinate system required by X11.\n- You tend to get an endless stream of X11 bugs that take further time and effort away from other tasks.\n- There aren't actually that many X11-only clients nowadays, and xwayland-satellite takes perfect care of most of those.\n- niri isn't a Big Serious Desktop Environment which Must Support All Use Cases (and is Backed By Some Corporation).\n\nAll in all, the situation works out in favor of avoiding Xwayland integration.\n\n<sup>Since: 25.08</sup> niri has seamless built-in xwayland-satellite integration that by and large works as well as built-in Xwayland in other compositors, solving the hurdle of having to set it up manually.\n\nI 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.\n\n### Can I enable blur behind semitransparent windows?\n\nNot yet, follow/upvote [this issue](https://github.com/niri-wm/niri/issues/54).\n\nThere's also [a PR](https://github.com/niri-wm/niri/pull/1634) adding blur to niri which you can build and run manually.\nKeep in mind that it's an experimental implementation that may have problems and performance concerns.\n\n### Can I make a window sticky / pinned / always on top / appear on all workspaces?\n\nNot yet, follow/upvote [this issue](https://github.com/niri-wm/niri/issues/932).\n\nYou can emulate this with a script that uses the niri IPC.\nFor example, [nirius](https://git.sr.ht/~tsdh/nirius) seems to have this feature (`toggle-follow-mode`).\n\n### How do I make the Bitwarden window in Firefox open as floating?\n\nFirefox 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.\n\nYou'll need to use a script, for example [this one](https://github.com/niri-wm/niri/discussions/1599) or other ones (search niri issues and discussions for Bitwarden).\n\n### Can I open a window directly in the current column / in the same column as another window?\n\nNo, but you can script the behavior you want with the [niri IPC](./IPC.md).\nListen to the event stream for a new window opening, then call an action like `consume-or-expel-window-left`.\n\nAdding this directly to niri is challenging:\n\n- The act of \"opening a window directly in some column\" by itself is quite involved. Niri will have to compute the exact initial window size provided how other windows in a column would resize in response. This logic exists, but it isn't directly pluggable to the code computing a size for a new window. Then, it'll need to handle all sorts of edge cases like the column disappearing, or new windows getting added to the column, before the target window had a chance to appear.\n- How do you indicate if a new window should spawn in an existing column (and in which one), as opposed to a new column? Different people seem to have different needs here (including very complex rules based on parent PID, etc.), and it's very unclear design-wise what kind of (simple) setting is actually needed and would be useful. See also https://github.com/niri-wm/niri/discussions/1125.\n\n### Why does moving the mouse against a monitor edge focus the next window, but only sometimes?\n\nThis can happen with [`focus-follows-mouse`](./Configuration:-Input.md#focus-follows-mouse).\nWhen using client-side decorations, windows are supposed to have some margins outside their geometry for the mouse resizing handles.\nThese margins \"peek out\" of the monitor edges since they're outside the window geometry, and `focus-follows-mouse` triggers when the mouse crosses them.\n\nIt doesn't always happen:\n\n- Some toolkits don't put resize handles outside the window geometry. Then there's no input area outside, so nowhere for `focus-follows-mouse` to trigger.\n- If the current window has its own margin for resizing, and it extends all the way to the monitor edge, then `focus-follows-mouse` won't trigger because the mouse will never leave the current window.\n\nTo fix this, you can:\n\n- Use `focus-follows-mouse max-scroll-amount=\"0%\"`, which will prevent `focus-follows-mouse` from triggering when it would cause scrolling.\n- Set `prefer-no-csd` which will generally cause clients to remove those resizing margins.\n\n### How do I recover from a dead screen locker / from a red screen?\n\nWhen your screen locker dies, you will be left with a red screen.\nThis is niri's locked session background.\n\nYou can recover from this by spawning a new screen locker.\nOne way is to switch to a different TTY (with a shortcut like <kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>F3</kbd>) and spawning a screen locker to niri's Wayland display, e.g. `WAYLAND_DISPLAY=wayland-1 swaylock`.\n\nAnother way is to set `allow-when-locked=true` on your screen locker bind, then you can press it on the red screen to get a fresh screen locker.\n```kdl\nbinds {\n    Super+Alt+L allow-when-locked=true { spawn \"swaylock\"; }\n}\n```\n\n### How do I change output configuration based on connected monitors?\n\nIf you require different output configurations depending on what outputs are connected then you can use [Kanshi](https://gitlab.freedesktop.org/emersion/kanshi).\n\nKanshi has its own simple configuration and communicates with niri via IPC. You may want to launch kanshi from the niri config.kdl e.g. `spawn-at-startup \"/usr/bin/kanshi\"`\n\nFor example, if you wish to scale your laptop display differently when an external monitor is connected, you might use a Kanshi config like this:\n```\nprofile {\n\toutput eDP-1 enable scale 1.0\n}\n\nprofile { \n\toutput HDMI-A-1 enable scale 1.0 position 0,0\n\toutput eDP-1 enable scale 1.25 position 1920,0\n}\n```\n\n### Why does Firefox or Thunderbird have 1 px smaller border?\n\nThey draw their own 1 px dark border around the window, which obscures one pixel of niri's border.\nIf you don't like this, set the [`clip-to-geometry true` window rule](./Configuration:-Window-Rules.md#clip-to-geometry).\n"
  },
  {
    "path": "docs/wiki/Floating-Windows.md",
    "content": "### Overview\n\n<sup>Since: 25.01</sup>\n\nFloating windows in niri always show on top of the tiled windows.\nThe floating layout does not scroll.\nEach workspace/monitor has its own floating layout, just like each workspace/monitor has its own tiling layout.\n\nNew windows will automatically float if they have a parent (e.g. dialogs) or if they are fixed size (e.g. splash screens).\nTo change a window between floating and tiling, you can use the `toggle-window-floating` bind or right click while dragging/moving the window.\nYou can also use the `open-floating true/false` window rule to either force a window to open as floating, or to disable the automatic floating logic.\n\nUse `switch-focus-between-floating-and-tiling` to switch the focus between the two layouts.\nWhen focused on the floating layout, binds (like `focus-column-right`) will operate on the floating window.\n\nYou can precisely position a floating window with a command like `niri msg action move-floating-window -x 100 -y 200`.\n"
  },
  {
    "path": "docs/wiki/Fullscreen-and-Maximize.md",
    "content": "There are several ways to make a window big on niri: maximizing the column, maximizing the window to edges, and fullscreening the window.\nLet's look at their differences.\n\n## Maximized (full-width) columns\n\nMaximizing the column via `maximize-column` (bound to <kbd>Mod</kbd><kbd>F</kbd> by default) expands its width to cover the whole screen.\nMaximized columns still leave space for [struts] and [gaps], and can contain multiple windows.\nThe windows retain their borders.\nThis is the simplest of the sizing modes, and is equivalent to `proportion 1.0` column width, or `set-column-width \"100%\"`.\n\n![Screenshot of a maximized column with two windows.](./img/maximized-column.png)\n\nYou can make a window open in a maximized column with the [`open-maximized true`](./Configuration:-Window-Rules.md#open-maximized) window rule.\n\n## Windows maximized to edges\n\n<sup>Since: 25.11</sup>\n\nYou can maximize an individual window via `maximize-window-to-edges`.\nThis is the same maximize as you can find on other desktop environments and operating systems: it expands a window to the edges of the available screen area.\nYou will still see your bar, but not struts, gaps, or borders.\n\nWindows are aware of their maximized-to-edges status and generally respond by squaring their corners.\nWindows can also control maximizing-to-edges: when you click on the square icon in the window's titlebar, or double-click on the titlebar, the window will request niri to maximize or unmaximize itself.\n\nYou can put multiple maximized windows into a [tabbed column](./Tabs.md), but not into a regular column.\n\n![Screenshot of a window maximized to edges.](./img/window-maximized-to-edges.png)\n\nYou can make a window open maximized-to-edges, or prevent a window from maximizing upon opening, with the [`open-maximized-to-edges`](./Configuration:-Window-Rules.md#open-maximized-to-edges) window rule.\n\n## Fullscreen windows\n\nWindows can go fullscreen, usually seen with video players, presentations or games.\nYou can also force a window to go fullscreen via `fullscreen-window` (bound to <kbd>Mod</kbd><kbd>Shift</kbd><kbd>F</kbd> by default).\nFullscreen windows cover the entire screen.\nSimilarly to maximize-to-edges, windows are aware of their fullscreen status, and can respond by hiding their titlebars or other parts of the UI.\n\nNiri renders a solid black backdrop behind fullscreen windows.\nThis backdrop helps match the screen size when the window itself remains too small (e.g. if you try to fullscreen a fixed-size dialog window), which is the behavior [defined by the Wayland protocol](https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_fullscreen).\n\nWhen a fullscreen window is focused and not animating, it will cover floating windows and the top layer-shell layer.\nIf you want for example your layer-shell notifications or launcher to appear over fullscreen windows, configure the respective tools to put them on the overlay layer-shell layer.\n\n![Screenshot of a fullscreen window.](./img/fullscreen-window.png)\n\nYou can make a window open fullscreen, or prevent a window from fullscreening upon opening, with the [`open-fullscreen`](./Configuration:-Window-Rules.md#open-fullscreen) window rule.\n\n## Common behaviors across fullscreen and maximize\n\nFullscreen or maximized-to-edges windows can only be in the scrolling layout.\nSo if you try to fullscreen or maximize a [floating window](./Floating-Windows.md), it'll move into the scrolling layout.\nThen, unfullscreening/unmaximizing will bring it back into the floating layout automatically.\n\nThanks to scrollable tiling, fullscreen and maximized windows remain a normal participant of the layout: you can scroll left and right from them and see other windows.\n\n![Screenshot of the overview showing a fullscreen window with other windows side by side.](./img/fullscreen-window-in-overview.png)\n\nFullscreen and maximize-to-edges are both special states that the windows are aware of and can control.\nWindows sometimes want to restore their fullscreen or, more frequently, maximized state when they open.\nThe best opportunity for this is during the *initial configure* sequence when the window tells niri everything it should know before opening the window.\nIf the window does this, then `open-maximized-to-edges` and `open-fullscreen` window rules have a chance to block or adjust the request.\n\nHowever, some clients tend to request to be maximized shortly *after* the initial configure sequence, when the niri already sent them the initial size request (sometimes even after showing on screen, resulting in a quick resize right after opening).\nFrom niri's point of view, the window is already open by this point, so if the window does this, then the `open-maximized-to-edges` and `open-fullscreen` window rules don't do anything.\n\n## Windowed fullscreen\n\n<sup>Since: 25.05</sup>\n\nNiri can also tell a window that it's in fullscreen without actually making it fullscreen, via the `toggle-windowed-fullscreen` action.\nThis is generally useful for screencasting browser-based presentations, when you want to hide the browser UI, but still have the window sized as a normal window.\n\nWhen in windowed fullscreen, you can use the niri action to maximize or unmaximize the window.\nWindow-side titlebar maximize buttons and gestures may not work, since the window will always think that it's in fullscreen.\n\nSee also windowed fullscreen on the [screencasting features wiki page](./Screencasting.md#windowed-fakedetached-fullscreen).\n\n\n[struts]: ./Configuration:-Layout.md#struts\n[gaps]: ./Configuration:-Layout.md#gaps\n"
  },
  {
    "path": "docs/wiki/Gestures.md",
    "content": "### Overview\n\nThere are several gestures in niri.\n\nAlso see the [gestures configuration](./Configuration:-Gestures.md) wiki page.\n\n### Mouse\n\n#### Interactive Move\n\n<sup>Since: 0.1.10</sup>\n\nYou can move windows by holding <kbd>Mod</kbd> and the left mouse button.\n\nYou can customize the look of the window insertion preview in the [`insert-hint` layout config](./Configuration:-Layout.md#insert-hint).\n\n<sup>Since: 25.01</sup> Right click while moving to toggle between floating and tiling layout to put the window into.\n\n#### Interactive Resize\n\n<sup>Since: 0.1.6</sup>\n\nYou can resize windows by holding <kbd>Mod</kbd> and the right mouse button.\n\n#### Reset Window Height\n\n<sup>Since: 0.1.6</sup>\n\nIf you double-click on a top or bottom tiled window resize edge, the window height will reset to automatic.\n\nThis works with both window-initiated resizes (when using client-side decorations), and niri-initiated <kbd>Mod</kbd> + right click resizes.\n\n#### Toggle Full Width\n\n<sup>Since: 0.1.6</sup>\n\nIf you double-click on a left or right tiled window resize edge, the column will expand to the full workspace width.\n\nThis works with both window-initiated resizes (when using client-side decorations), and niri-initiated <kbd>Mod</kbd> + right click resizes.\n\n#### Horizontal View Movement\n\n<sup>Since: 0.1.6</sup>\n\nMove the view horizontally by holding <kbd>Mod</kbd> and the middle mouse button (or the wheel) and dragging the mouse horizontally.\n\n#### Workspace Switch\n\n<sup>Since: 0.1.7</sup>\n\nSwitch workspaces by holding <kbd>Mod</kbd> and the middle mouse button (or the wheel) and dragging the mouse vertically.\n\n### Touchpad\n\n#### Workspace Switch\n\nSwitch workspaces with three-finger vertical swipes.\n\n#### Horizontal View Movement\n\nMove the view horizontally with three-finger horizontal swipes.\n\n#### Open and Close the Overview\n\n<sup>Since: 25.05</sup>\n\nOpen and close the overview with a four-finger vertical swipe.\n\n### All Pointing Devices\n\n#### Drag-and-Drop Edge View Scroll\n\n<sup>Since: 25.02</sup>\n\nScroll the tiling view when moving the mouse cursor against a monitor edge during drag-and-drop (DnD).\nAlso works on a touchscreen.\n\n#### Drag-and-Drop Edge Workspace Switch\n\n<sup>Since: 25.05</sup>\n\nScroll the workspaces up/down when moving the mouse cursor against a monitor edge during drag-and-drop (DnD) while in the overview.\nAlso works on a touchscreen.\n\n#### Drag-and-Drop Hold to Activate\n\n<sup>Since: 25.05</sup>\n\nWhile drag-and-dropping, hold your mouse over a window to activate it.\nThis will bring a floating window to the top for example.\n\nIn the overview, you can also hold the mouse over a workspace to switch to it.\n\n#### Hot Corner to Toggle the Overview\n\n<sup>Since: 25.05</sup>\n\nPut your mouse at the very top-left corner of a monitor to toggle the overview.\nAlso works during drag-and-dropping something.\n"
  },
  {
    "path": "docs/wiki/Getting-Started.md",
    "content": "## Quick start\n\nUse these commands to install niri with [DankMaterialShell](https://github.com/AvengeMedia/DankMaterialShell) for a fairly out-of-the-box experience.\n\nFedora:\n```\nsudo dnf copr enable avengemedia/dms\nsudo dnf install niri dms\nsystemctl --user add-wants niri.service dms\n```\n\nArch Linux (via [paru](https://github.com/morganamilo/paru)):\n```\nsudo pacman -Syu niri xwayland-satellite xdg-desktop-portal-gnome xdg-desktop-portal-gtk alacritty\nparu -S dms-shell-bin matugen cava qt6-multimedia-ffmpeg\nsystemctl --user add-wants niri.service dms\n```\n\nUbuntu 25.10 and above:\n```\nsudo add-apt-repository ppa:avengemedia/danklinux\nsudo add-apt-repository ppa:avengemedia/dms\nsudo apt install niri dms\n```\n\nAfter running these commands, log out, choose Niri in your display manager, and log back in.\nOr, if not using a display manager, run `niri-session` on a TTY.\n\nThe default niri config will run Waybar, so you might get two bars on screen.\nTo fix this, stop Waybar with `pkill waybar` command, then open `~/.config/niri/config.kdl` and delete the `spawn-at-startup \"waybar\"` line.\n\nCheck the DankMaterialShell's [compositor setup page](https://danklinux.com/docs/dankmaterialshell/compositors#niri-configuration) to learn how to configure DMS-specific binds and other niri integrations.\n\n## Slower and more considered start\n\nThe easiest way to get niri is to install one of the distribution packages.\nHere are some of them: [Fedora COPR](https://copr.fedorainfracloud.org/coprs/yalter/niri/) and [nightly COPR](https://copr.fedorainfracloud.org/coprs/yalter/niri-git/) (which I maintain myself), [NixOS Flake](https://github.com/sodiboo/niri-flake), and some more from repology below, including a [pacstall package](https://pacstall.dev/packages/niri/) for Debian-based distros.\nSee the [Building](#building) section if you'd like to compile niri yourself and the [Packaging niri](./Packaging-niri.md) page if you want to package niri.\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/niri.svg)](https://repology.org/project/niri/versions)\n\nAfter installing, start niri from your display manager like GDM.\nPress <kbd>Super</kbd><kbd>T</kbd> to run a terminal ([Alacritty]) and <kbd>Super</kbd><kbd>D</kbd> to run an application launcher ([fuzzel]).\nTo exit niri, press <kbd>Super</kbd><kbd>Shift</kbd><kbd>E</kbd>.\n\nIf you're not using a display manager, you should run `niri-session` (systemd/dinit) or `niri --session` (others) from a TTY.\nThe `--session` flag will make niri import its environment variables globally into the system manager and D-Bus, and start its D-Bus services.\nThe `niri-session` script will additionally start niri as a systemd/dinit service, which starts up a graphical session target required by some services like portals.\n\nYou can also run `niri` inside an existing desktop session.\nThen it will open as a window, where you can give it a try.\nNote that this windowed mode is mainly meant for development, so it is a bit buggy (in particular, there are issues with hotkeys).\n\nNext, see the [list of important software](./Important-Software.md) required for normal desktop use, like a notification daemon and portals.\nAlso, check the [configuration introduction](./Configuration:-Introduction.md) page to get started configuring niri.\nThere you can find links to other pages containing thorough documentation and examples for all options.\nFinally, the [Xwayland](./Xwayland.md) page explains how to run X11 applications on niri.\n\n### Desktop environments\n\nSome desktop environments and shells work with niri and can give a more out-of-the-box experience:\n\n- [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.\n- 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.\n- 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).\n- 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).\n\n### NVIDIA\n\nThe NVIDIA drivers currently have an issue with high VRAM usage due to a heap reuse quirk.\nYou're recommended to apply a manual fix documented [here](./Nvidia.md) if you run niri on an NVIDIA GPU.\n\nNVIDIA GPUs can have problems running niri (for example, the screen remains black upon starting from a TTY).\nSometimes, the problems can be fixed.\nYou can try the following:\n\n1. Update NVIDIA drivers. You need a GPU and drivers recent enough to support GBM.\n2. Make sure kernel modesetting is enabled. This usually involves adding `nvidia-drm.modeset=1` to the kernel command line. Find and follow a guide for your distribution. Guides from other Wayland compositors can help.\n\n### Asahi, ARM, and other kmsro devices\n\nOn some of these systems, niri fails to correctly detect the primary render device.\nIf you're getting a black screen when starting niri on a TTY, you can try to set the device manually.\n\nFirst, find which devices you have:\n\n```\n$ ls -l /dev/dri/\ndrwxr-xr-x@       - root 14 мая 07:07 by-path\ncrw-rw----@   226,0 root 14 мая 07:07 card0\ncrw-rw----@   226,1 root 14 мая 07:07 card1\ncrw-rw-rw-@ 226,128 root 14 мая 07:07 renderD128\ncrw-rw-rw-@ 226,129 root 14 мая 07:07 renderD129\n```\n\nYou will likely have one `render` device and two `card` devices.\n\nOpen the niri config file at `~/.config/niri/config.kdl` and put your `render` device path like this:\n\n```kdl\ndebug {\n    render-drm-device \"/dev/dri/renderD128\"\n}\n```\n\nSave, then try to start niri again.\nIf you still get a black screen, try using each of the `card` devices.\n\n### Nix/NixOS\n\nThere's a common problem of mesa drivers going out of sync with niri, so make sure your system mesa version matches the niri mesa version.\nWhen this happens, you usually see a black screen when trying to start niri from a TTY.\n\nAlso, on Intel graphics, you may need a workaround described [here](https://wiki.nixos.org/wiki/Intel_Graphics).\n\n### Virtual Machines\n\nTo run niri in a VM, make sure to enable 3D acceleration.\n\n## Main Default Hotkeys\n\nWhen running on a TTY, the Mod key is <kbd>Super</kbd>.\nWhen running in a window, the Mod key is <kbd>Alt</kbd>.\n\nThe general system is: if a hotkey switches somewhere, then adding <kbd>Ctrl</kbd> will move the focused window or column there.\n\n| Hotkey | Description |\n| ------ | ----------- |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>/</kbd> | Show a list of important niri hotkeys |\n| <kbd>Mod</kbd><kbd>T</kbd> | Spawn `alacritty` (terminal) |\n| <kbd>Mod</kbd><kbd>D</kbd> | Spawn `fuzzel` (application launcher) |\n| <kbd>Super</kbd><kbd>Alt</kbd><kbd>L</kbd> | Spawn `swaylock` (screen locker) |\n| <kbd>Mod</kbd><kbd>Q</kbd> | Close the focused window |\n| <kbd>Mod</kbd><kbd>H</kbd> or <kbd>Mod</kbd><kbd>←</kbd> | Focus the column to the left |\n| <kbd>Mod</kbd><kbd>L</kbd> or <kbd>Mod</kbd><kbd>→</kbd> | Focus the column to the right |\n| <kbd>Mod</kbd><kbd>J</kbd> or <kbd>Mod</kbd><kbd>↓</kbd> | Focus the window below in a column |\n| <kbd>Mod</kbd><kbd>K</kbd> or <kbd>Mod</kbd><kbd>↑</kbd> | Focus the window above in a column |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>H</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>←</kbd> | Move the focused column to the left |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>L</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>→</kbd> | Move the focused column to the right |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>J</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>↓</kbd> | Move the focused window below in a column |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>K</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>↑</kbd> | Move the focused window above in a column |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>H</kbd><kbd>J</kbd><kbd>K</kbd><kbd>L</kbd> or <kbd>Mod</kbd><kbd>Shift</kbd><kbd>←</kbd><kbd>↓</kbd><kbd>↑</kbd><kbd>→</kbd> | Focus the monitor to the side |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>H</kbd><kbd>J</kbd><kbd>K</kbd><kbd>L</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>Shift</kbd><kbd>←</kbd><kbd>↓</kbd><kbd>↑</kbd><kbd>→</kbd> | Move the focused column to the monitor to the side |\n| <kbd>Mod</kbd><kbd>U</kbd> or <kbd>Mod</kbd><kbd>PageDown</kbd> | Switch to the workspace below |\n| <kbd>Mod</kbd><kbd>I</kbd> or <kbd>Mod</kbd><kbd>PageUp</kbd> | Switch to the workspace above |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>U</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>PageDown</kbd> | Move the focused column to the workspace below |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>I</kbd> or <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>PageUp</kbd> | Move the focused column to the workspace above |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>U</kbd> or <kbd>Mod</kbd><kbd>Shift</kbd><kbd>PageDown</kbd> | Move the focused workspace down |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>I</kbd> or <kbd>Mod</kbd><kbd>Shift</kbd><kbd>PageUp</kbd> | Move the focused workspace up |\n| <kbd>Mod</kbd><kbd>,</kbd> | Consume the window to the right into the focused column |\n| <kbd>Mod</kbd><kbd>.</kbd> | Expel the bottom window in the focused column into its own column |\n| <kbd>Mod</kbd><kbd>[</kbd> | Consume or expel the focused window to the left |\n| <kbd>Mod</kbd><kbd>]</kbd> | Consume or expel the focused window to the right |\n| <kbd>Mod</kbd><kbd>R</kbd> | Toggle between preset column widths |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>R</kbd> | Toggle between preset column heights |\n| <kbd>Mod</kbd><kbd>F</kbd> | Maximize column |\n| <kbd>Mod</kbd><kbd>C</kbd> | Center column within view |\n| <kbd>Mod</kbd><kbd>-</kbd> | Decrease column width by 10% |\n| <kbd>Mod</kbd><kbd>=</kbd> | Increase column width by 10% |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>-</kbd> | Decrease window height by 10% |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>=</kbd> | Increase window height by 10% |\n| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>R</kbd> | Reset window height back to automatic |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>F</kbd> | Toggle full-screen on the focused window |\n| <kbd>Mod</kbd><kbd>V</kbd> | Move the focused window between the floating and the tiling layout |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>V</kbd> | Switch focus between the floating and the tiling layout |\n| <kbd>PrtSc</kbd> | Take an area screenshot. Select the area to screenshot with mouse, then press Space to save the screenshot, or Escape to cancel |\n| <kbd>Alt</kbd><kbd>PrtSc</kbd> | Take a screenshot of the focused window to clipboard and to `~/Pictures/Screenshots/` |\n| <kbd>Ctrl</kbd><kbd>PrtSc</kbd> | Take a screenshot of the focused monitor to clipboard and to `~/Pictures/Screenshots/` |\n| <kbd>Mod</kbd><kbd>Shift</kbd><kbd>E</kbd> or <kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>Delete</kbd> | Exit niri |\n\n## Building\n\nFirst, install the dependencies for your distribution.\n\n- Ubuntu 24.04:\n\n    ```sh\n    sudo apt-get install -y 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\n    ```\n\n- Fedora:\n\n    ```sh\n    sudo dnf install gcc libudev-devel libgbm-devel libxkbcommon-devel wayland-devel libinput-devel dbus-devel systemd-devel libseat-devel pipewire-devel pango-devel cairo-gobject-devel clang libdisplay-info-devel\n    ```\n\nNext, get latest stable Rust: https://rustup.rs/\n\nThen, build niri with `cargo build --release`.\n\nCheck Cargo.toml for a list of build features.\nFor example, you can replace systemd integration with dinit integration using `cargo build --release --no-default-features --features dinit,dbus,xdp-gnome-screencast`.\n\n> [!WARNING]\n> Do NOT build with `--all-features`!\n>\n> Some features are meant only for development use.\n> For example, one of the features enables collection of profiling data into a memory buffer that will grow indefinitely until you run out of memory.\n\n### NixOS/Nix\n\nWe have a community-maintained flake which provides a devshell with required dependencies. Use `nix build` to build niri, and then run `./results/bin/niri`.\n\nIf you're not on NixOS, you may need [NixGL](https://github.com/nix-community/nixGL) to run the resulting binary:\n\n```sh\nnix run --impure github:guibou/nixGL -- ./results/bin/niri\n```\n\n### Manual Installation\n\nIf installing directly without a package, the recommended file destinations are slightly different.\nIn this case, put the files in the directories indicated in the table below.\nThese may vary depending on your distribution.\n\nDon't forget to make sure that the path to `niri` in niri.service is correct.\nThis defaults to `/usr/bin/niri`.\n\n| File | Destination |\n| ---- | ----------- |\n| `target/release/niri` | `/usr/local/bin/` |\n| `resources/niri-session` | `/usr/local/bin/` |\n| `resources/niri.desktop`  | `/usr/local/share/wayland-sessions/` |\n| `resources/niri-portals.conf` | `/usr/local/share/xdg-desktop-portal/` |\n| `resources/niri.service` (systemd) | `/etc/systemd/user/` |\n| `resources/niri-shutdown.target` (systemd) | `/etc/systemd/user/` |\n| `resources/dinit/niri` (dinit) | `/etc/dinit.d/user/` |\n| `resources/dinit/niri.target` (dinit) | `/etc/dinit.d/user/` |\n\n[Alacritty]: https://github.com/alacritty/alacritty\n[fuzzel]: https://codeberg.org/dnkl/fuzzel\n"
  },
  {
    "path": "docs/wiki/IPC.md",
    "content": "You can communicate with the running niri instance over an IPC socket.\nCheck `niri msg --help` for available commands.\n\nThe `--json` flag prints the response in JSON, rather than formatted.\nFor example, `niri msg --json outputs`.\n\n> [!TIP]\n> If you're getting parsing errors from `niri msg` after upgrading niri, make sure that you've restarted niri itself.\n> You might be trying to run a newer `niri msg` against an older `niri` compositor.\n\n### Event Stream\n\n<sup>Since: 0.1.9</sup>\n\nWhile most niri IPC requests return a single response, the event stream request will make niri continuously stream events into the IPC connection until it is closed.\nThis is useful for implementing various bars and indicators that update as soon as something happens, without continuous polling.\n\nThe event stream IPC is designed to give you the complete current state up-front, then follow up with updates to that state.\nThis way, your state can never \"desync\" from niri, and you don't need to make any other IPC information requests.\n\nWhere reasonable, event stream state updates are atomic, though this is not always the case.\nFor example, a window may end up with a workspace id for a workspace that had already been removed.\nThis can happen if the corresponding workspaces-changed event arrives before the corresponding window-changed event.\n\nTo get a taste of the events, run `niri msg event-stream`.\nThough, this is more of a debug function than anything.\nYou can get raw events from `niri msg --json event-stream`, or by connecting to the niri socket and requesting an event stream manually.\n\nYou can find the full list of events along with documentation [here](https://niri-wm.github.io/niri/niri_ipc/enum.Event.html).\n\n### Programmatic Access\n\n`niri msg --json` is a thin wrapper over writing and reading to a socket.\nWhen implementing more complex scripts and modules, you're encouraged to access the socket directly.\n\nConnect to the UNIX domain socket located at `$NIRI_SOCKET` in the filesystem.\nWrite your request encoded in JSON on a single line, followed by a newline character, or by flushing and shutting down the write end of the connection.\nRead the reply as JSON, also on a single line.\n\nYou can use `socat` to test communicating with niri directly:\n\n```sh\n$ socat STDIO \"$NIRI_SOCKET\"\n\"FocusedWindow\"\n{\"Ok\":{\"FocusedWindow\":{\"id\":12,\"title\":\"t socat STDIO /run/u ~\",\"app_id\":\"Alacritty\",\"workspace_id\":6,\"is_focused\":true}}}\n```\n\nThe reply is an `Ok` or an `Err` wrapping the same JSON object as you get from `niri msg --json`.\n\nFor more complex requests, you can use `socat` to find how `niri msg` formats them:\n\n```sh\n$ socat STDIO UNIX-LISTEN:temp.sock\n# then, in a different terminal:\n$ env NIRI_SOCKET=./temp.sock niri msg action focus-workspace 2\n# then, look in the socat terminal:\n{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Index\":2}}}}\n```\n\nYou can find all available requests and response types in the [niri-ipc sub-crate documentation](https://niri-wm.github.io/niri/niri_ipc/).\n\n### Backwards Compatibility\n\nThe JSON output *should* remain stable, as in:\n\n- existing fields and enum variants should not be renamed\n- non-optional existing fields should not be removed\n\nHowever, new fields and enum variants will be added, so you should handle unknown fields or variants gracefully where reasonable.\n\nThe formatted/human-readable output (i.e. without `--json` flag) is **not** considered stable.\nPlease prefer the JSON output for scripts, since I reserve the right to make any changes to the human-readable output.\n\nThe `niri-ipc` sub-crate (like other niri sub-crates) is *not* API-stable in terms of the Rust semver; rather, it follows the version of niri itself.\nIn particular, new struct fields and enum variants will be added.\n"
  },
  {
    "path": "docs/wiki/Important-Software.md",
    "content": "Since niri is not a complete desktop environment, you will very likely want to run the following software to make sure that other apps work fine.\n\n### Notification Daemon\n\nMany apps need one. For example, [mako](https://github.com/emersion/mako) works well. Use [a systemd setup](./Example-systemd-Setup.md) or [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup).\n\n### Portals\n\nThese provide a cross-desktop API for apps to use for various things like file pickers or UI settings. Flatpak apps in particular require working portals.\n\nPortals **require** [running niri as a session](./Getting-Started.md), which means through the `niri-session` script or from a display manager. You will want the following portals installed:\n\n* `xdg-desktop-portal-gtk`: implements most of the basic functionality, this is the \"default fallback portal\".\n* `xdg-desktop-portal-gnome`: required for screencasting support.\n* `gnome-keyring`: implements the Secret portal, required for certain apps to work.\n\nThen systemd should start them on-demand automatically. These particular portals are configured in `niri-portals.conf` which [must be installed](./Getting-Started.md#manual-installation) in the correct location.\n\nSince we're using `xdg-desktop-portal-gnome`, Flatpak apps will read the GNOME UI settings. For example, to enable the dark style, run:\n\n```\ndconf write /org/gnome/desktop/interface/color-scheme '\"prefer-dark\"'\n```\n\nNote that if you're using the provided `resources/niri-portals.conf`, you also need to install the `nautilus` file manager in order for file chooser dialogues to work properly. This is necessary because xdg-desktop-portal-gnome uses nautilus as the file chooser by default starting from version 47.0.\n\nIf you do not want to install `nautilus` (say you use `nemo` instead), you can set `org.freedesktop.impl.portal.FileChooser=gtk;` in `niri-portals.conf` to use the GTK portal for file chooser dialogues.\n\n> [!WARNING]\n> Do not set the `GDK_BACKEND` environment variable globally as this will break the screencast portal.\n\n### Authentication Agent\n\nRequired when apps need to ask for root permissions. Something like `plasma-polkit-agent` works fine. Start it [with systemd](./Example-systemd-Setup.md) or with [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup).\n\nNote that to start `plasma-polkit-agent` with systemd on Fedora, you'll need to override its systemd service to add the correct dependency. Run:\n\n```\nsystemctl --user edit --full plasma-polkit-agent.service\n```\n\nThen add `After=graphical-session.target`.\n\n### Xwayland\n\nTo run X11 apps like Steam or Discord, you can use [xwayland-satellite].\nCheck [the Xwayland wiki page](./Xwayland.md) for instructions.\n\n[xwayland-satellite]: https://github.com/Supreeeme/xwayland-satellite\n"
  },
  {
    "path": "docs/wiki/Integrating-niri.md",
    "content": "This page contains various bits of information helpful for integrating niri in a distribution.\nFirst, for creating a niri package, see the [Packaging](./Packaging-niri.md) page.\n\n### Configuration\n\nNiri will load configuration from `$XDG_CONFIG_HOME/niri/config.kdl` or `~/.config/niri/config.kdl`, falling back to `/etc/niri/config.kdl`.\nIf both of these files are missing, niri will create `$XDG_CONFIG_HOME/niri/config.kdl` with the contents of [the default configuration file](https://github.com/niri-wm/niri/blob/main/resources/default-config.kdl), which are embedded into the niri binary at build time.\n\nThis means that you can customize your distribution defaults by creating `/etc/niri/config.kdl`.\nWhen this file is present, niri *will not* automatically create a config at `~/.config/niri/`, so you'll need to direct your users how to do it themselves.\n\nKeep in mind that we update the default config in new releases, so if you have a custom `/etc/niri/config.kdl`, you likely want to inspect and apply the relevant changes too.\n\nThe default configuration locations can be overridden with the `NIRI_CONFIG` environment variable.\n\n<sup>Since: next release</sup> You can also change the configuration path at runtime via the niri IPC or using the command `niri msg action load-config-file --path <path-to-config.kdl>`.\n\n<sup>Since: 25.11</sup> You can split the niri config file into multiple files using [`include`](./Configuration:-Include.md).\n\n### Xwayland\n\nXwayland is required for running X11 apps and games, and also the Orca screen reader.\n\n<sup>Since: 25.08</sup> Niri integrates with [xwayland-satellite](https://github.com/Supreeeme/xwayland-satellite) out of the box.\nThe integration requires xwayland-satellite >= 0.7 available in `$PATH`.\nPlease consider making niri depend on (or at least recommend) the xwayland-satellite package.\nIf you had a custom config which manually started `xwayland-satellite` and set `$DISPLAY`, you should remove those customizations for the automatic integration to work.\n\nYou can change the path where niri looks for xwayland-satellite using the [`xwayland-satellite` top-level option](./Configuration:-Miscellaneous.md#xwayland-satellite).\n\n### Keyboard layout\n\n<sup>Since: 25.08</sup> By default (unless [manually configured](./Configuration:-Input.md#layout) otherwise), niri reads keyboard layout settings from systemd-localed at `org.freedesktop.locale1` over D-Bus.\nMake sure your system installer sets the keyboard layout via systemd-localed, and niri should pick it up.\n\n### Autostart\n\nNiri works with the normal systemd autostart.\nThe default [niri.service](https://github.com/niri-wm/niri/blob/main/resources/niri.service) brings up `graphical-session.target` as well as `xdg-desktop-autostart.target`.\n\nTo make a program run at niri startup without editing the niri config, you can either link its .desktop to `~/.config/autostart/`, or use a .service file with `WantedBy=graphical-session.target`.\nSee the [example systemd setup](./Example-systemd-Setup.md) page for some examples.\n\nIf this is inconvenient, you can also add [`spawn-at-startup`](./Configuration:-Miscellaneous.md#spawn-at-startup) lines in the niri config.\n\n### Screen readers\n\n<sup>Since: 25.08</sup> Niri works with the [Orca](https://orca.gnome.org) screen reader.\nPlease see the [Accessibility](./Accessibility.md) page for details and advice for accessibility-focused distributions.\n\n### Desktop components\n\nYou very likely want to run at least a notification daemon, portals, and an authentication agent.\nThis is detailed on the [Important Software](./Important-Software.md) page.\n\nOn top of that, you may want to preconfigure some desktop shell components to make the experience less barebones.\nNiri's default config spawns [Waybar](https://github.com/Alexays/Waybar), which is a good starting point, but you may want to consider changing its default configuration to be less of a kitchen sink, and adding the `niri/workspaces` module.\nYou will probably also want a desktop background tool ([swaybg](https://github.com/swaywm/swaybg) or [awww (which used to be swww)](https://codeberg.org/LGFae/awww/)), and a nicer screen locker (compared to the default `swaylock`), like [hyprlock](https://github.com/hyprwm/hyprlock/).\n\nAlternatively, some desktop environments and shells work with niri, and can give a more cohesive experience in one package:\n\n- [LXQt](https://lxqt-project.org/) officially supports niri, see [their wiki](https://lxqt-project.org/wiki/Wayland-Session) for details on setting it up.\n- 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.\n- 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).\n- 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).\n"
  },
  {
    "path": "docs/wiki/Layer‐Shell-Components.md",
    "content": "Things to keep in mind with layer-shell components (bars, launchers, etc.):\n\n1. When a [full-screen](./Fullscreen-and-Maximize.md) window is active and covers the entire screen, it will render above the top layer, and it will be prioritized for keyboard focus. If your launcher uses the top layer, and you try to run it while looking at a full-screen window, it won't show up. Only the overlay layer will show up on top of full-screen windows.\n1. Components on the bottom and background layers will receive *on-demand* keyboard focus as expected. However, they will only receive *exclusive* keyboard focus when there are no windows on the workspace.\n1. When opening the [Overview](./Overview.md), components on the bottom and background layers will zoom out and remain on the workspaces, while the top and overlay layers remain on top of the Overview. So, if you want the bar to remain on top, put it on the *top* layer.\n"
  },
  {
    "path": "docs/wiki/Name-and-Logo.md",
    "content": "The name \"niri\" is canonically written in lower-case, but feel free to capitalize it if you'd like, especially at the start of sentences where the grammatical rules require it.\nThis name is not intended to mean or stand for anything.\n\nOur logo comes in four versions: full-sized, simple full-sized, icon, and simple icon.\nThe simple versions are single-color and suitable for smaller sizes.\n\n|        | full-sized                     | icon                           |\n|--------|:------------------------------:|:------------------------------:|\n| normal | ![](./logo/niri-logo.svg)      | ![](./logo/niri-icon.svg)      |\n| simple | ![](./logo/niri-logo-smol.svg) | ![](./logo/niri-icon-smol.svg) |\n\nThe logo is intentionally recolorable.\nIn fact, there's [a webpage](https://nirilogo.raurutuchr.ink) that lets you quickly adjust the color and download an SVG.\n\nAll versions of the logo are licensed under [CC BY-SA](https://creativecommons.org/licenses/by-sa/4.0/).\nThe full-sized logo is based on the [Cherry Bomb One](https://github.com/satsuyako/CherryBomb) font, licensed under the [SIL Open Font License 1.1](https://openfontlicense.org/).\n"
  },
  {
    "path": "docs/wiki/Nvidia.md",
    "content": "### High VRAM usage fix\n\nPresently, 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).\n\nLuckily, you can mitigate this by configuring the NVIDIA drivers with a per-process application profile as follows:\n\n* `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)\n* 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`:\n    \n    ```json\n    {\n        \"rules\": [\n            {\n                \"pattern\": {\n                    \"feature\": \"procname\",\n                    \"matches\": \"niri\"\n                },\n                \"profile\": \"Limit Free Buffer Pool On Wayland Compositors\"\n            }\n        ],\n        \"profiles\": [\n            {\n                \"name\": \"Limit Free Buffer Pool On Wayland Compositors\",\n                \"settings\": [\n                    {\n                        \"key\": \"GLVidHeapReuseRatio\",\n                        \"value\": 0\n                    }\n                ]\n            }\n        ]\n    }\n    ```\n    \n    (The file in `/etc/nvidia/nvidia-application-profiles-rc.d/` can be named anything, and does not actually need an extension).\n\nRestart niri after writing the config file to apply the change.\n\nThe 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.\n\nThe 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. \n\n### Screencast flickering fix\n\n<sup>Until: 25.08</sup>\n\nIf you have screencast glitches or flickering on NVIDIA, set this in the niri config:\n\n```kdl,must-fail\ndebug {\n    wait-for-frame-completion-in-pipewire\n}\n```\n\nThis debug flag has since been removed because the problem was properly fixed in niri.\n"
  },
  {
    "path": "docs/wiki/Overview.md",
    "content": "### Overview\n\n<sup>Since: 25.05</sup>\n\nThe Overview is a zoomed-out view of your workspaces and windows.\nIt lets you see what's going on at a glance, navigate, and drag windows around.\n\n<video controls src=\"https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995\">\n\nhttps://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995\n\n</video>\n\nOpen it with the `toggle-overview` bind, via the top-left hot corner, or using a touchpad four-finger swipe up.\nWhile in the overview, all keyboard shortcuts keep working, while pointing devices get easier:\n\n- Mouse: left click and drag windows to move them, right click and drag to scroll workspaces left/right, scroll to switch workspaces (no holding Mod required).\n- Touchpad: two-finger scrolling that matches the normal three-finger gestures.\n- Touchscreen: one-finger scrolling, or one-finger long press to move a window.\n\n> [!TIP]\n> The overview needs to draw a background under every workspace.\n> So, layer-shell surfaces work this way: the *background* and *bottom* layers zoom out together with the workspaces, while the *top* and *overlay* layers remain on top of the overview.\n>\n> Put your bar on the *top* layer.\n\nDrag-and-drop will scroll the workspaces up/down in the overview, and will activate a workspace when holding it for a moment.\nCombined with the hot corner, this lets you do a mouse-only DnD across workspaces.\n\n<video controls src=\"https://github.com/user-attachments/assets/5f09c5b7-ff40-462b-8b9c-f1b8073a2cbb\">\n\nhttps://github.com/user-attachments/assets/5f09c5b7-ff40-462b-8b9c-f1b8073a2cbb\n\n</video>\n\nYou can also drag-and-drop a window to a new workspace above, below, or between existing workspaces.\n\n<video controls src=\"https://github.com/user-attachments/assets/b76d5349-aa20-4889-ab90-0a51554c789d\">\n\nhttps://github.com/user-attachments/assets/b76d5349-aa20-4889-ab90-0a51554c789d\n\n</video>\n\n### Configuration\n\nSee the full documentation for the `overview {}` section [here](./Configuration:-Miscellaneous.md#overview).\n\nYou can set the zoom-out level like this:\n\n```kdl\n// Make workspaces four times smaller than normal in the overview.\noverview {\n    zoom 0.25\n}\n```\n\nTo change the color behind the workspaces, use the `backdrop-color` setting:\n\n```kdl\n// Make the backdrop light.\noverview {\n    backdrop-color \"#777777\"\n}\n```\n\nYou can also disable the hot corner:\n\n```kdl\n// Disable the hot corners.\ngestures {\n    hot-corners {\n        off\n    }\n}\n```\n\n### Backdrop customization\n\nApart from setting a custom backdrop color like described above, you can also put a layer-shell wallpaper into the backdrop with a [layer rule](./Configuration:-Layer-Rules.md#place-within-backdrop), for example:\n\n```kdl\n// Put swaybg inside the overview backdrop.\nlayer-rule {\n    match namespace=\"^wallpaper$\"\n    place-within-backdrop true\n}\n```\n\nThis will only work for *background* layer surfaces that ignore exclusive zones (typical for wallpaper tools).\n\nYou can run two different wallpaper tools (like swaybg and awww), one for the backdrop and one for the normal workspace background.\nThis way you could set the backdrop one to a blurred version of the wallpaper for a nice effect.\n\nYou can also combine this with a transparent background color if you don't like the wallpaper moving together with workspaces:\n\n```kdl\n// Make the wallpaper stationary, rather than moving with workspaces.\nlayer-rule {\n    // This is for swaybg; change for other wallpaper tools.\n    // Find the right namespace by running niri msg layers.\n    match namespace=\"^wallpaper$\"\n    place-within-backdrop true\n}\n\n// Set transparent workspace background color.\nlayout {\n    background-color \"transparent\"\n}\n\n// Optionally, disable the workspace shadows in the overview.\noverview {\n    workspace-shadow {\n        off\n    }\n}\n```\n"
  },
  {
    "path": "docs/wiki/Packaging-niri.md",
    "content": "### Overview\n\nWhen building niri, check `Cargo.toml` for a list of build features.\nFor example, you can replace systemd integration with dinit integration using `cargo build --release --no-default-features --features dinit,dbus,xdp-gnome-screencast`.\nThe defaults however should work fine for most distributions.\n\n> [!WARNING]\n> Do NOT build with `--all-features`!\n>\n> Some features are meant only for development use.\n> For example, one of the features enables collection of profiling data into a memory buffer that will grow indefinitely until you run out of memory.\n\nThe `niri-visual-tests` sub-crate/binary is development-only and should not be packaged.\n\nThe recommended way to package niri is so that it runs as a standalone desktop session.\nTo do that, put files into the correct directories according to this table.\n\n| File | Destination |\n| ---- | ----------- |\n| `target/release/niri` | `/usr/bin/` |\n| `resources/niri-session` | `/usr/bin/` |\n| `resources/niri.desktop` | `/usr/share/wayland-sessions/` |\n| `resources/niri-portals.conf` | `/usr/share/xdg-desktop-portal/` |\n| `resources/niri.service` (systemd) | `/usr/lib/systemd/user/` |\n| `resources/niri-shutdown.target` (systemd) | `/usr/lib/systemd/user/` |\n| `resources/dinit/niri` (dinit) | `/usr/lib/dinit.d/user/` |\n| `resources/dinit/niri.target` (dinit) | `/usr/lib/dinit.d/user/` |\n\nDoing this will make niri appear in GDM and other display managers.\n\nSee the [Integrating niri](./Integrating-niri.md) page for further information on distribution integration.\n\n### Recommended dependencies\n\nFirst of all, make sure niri depends on `libwayland-server`.\nThis library is currently loaded dynamically, so it's not picked up as a dependency at niri build time.\n\nThen, the following dependencies are optional, but strongly recommended.\nSet them as automatically-installed optional dependencies, if possible.\n\n- `xwayland-satellite`: required to run X11 applications (Steam, Discord, etc.).\n- `xdg-desktop-portal-gnome`: required for screencasting.\n- `xdg-desktop-portal-gtk`: configured as the fallback portal in `niri-portals.conf`.\n(This is in general the standard fallback portal that you want installed.)\n- `gnome-keyring`: configured as the Secret portal provider in `niri-portals.conf`.\n- Your distro's GPU driver package, such as `mesa-dri-drivers` and `mesa-libEGL`.\nWorking hardware acceleration is required for running niri.\n- Some notification daemon like `mako`, generally required for apps to work correctly.\n\nFinally, you may want to auto-install some of the applications bound in niri's [default configuration file](https://github.com/niri-wm/niri/blob/main/resources/default-config.kdl) (search for `spawn`), such as `alacritty` and `fuzzel`.\n\n### Running tests\n\nA bulk of our tests spawn niri compositor instances and test Wayland clients.\nThis does not require a graphical session, however due to test parallelism, it can run into file descriptor limits on high core count systems.\n\nIf you run into this problem, you may need to limit not just the Rust test harness thread count, but also the Rayon thread count, since some niri tests use internal Rayon threading:\n\n```\n$ export RAYON_NUM_THREADS=2\n...proceed to run cargo test, perhaps with --test-threads=2\n```\n\nDon't forget to exclude the development-only `niri-visual-tests` crate when running tests.\n\nSome tests require surfaceless EGL to be available at test time.\nIf this is problematic, you can skip them like so:\n\n```\n$ cargo test -- --skip=::egl\n```\n\nYou may also want to set the `RUN_SLOW_TESTS=1` environment variable to run the slower tests.\n\n### Version string\n\nThe niri version string includes its version and commit hash:\n\n```\n$ niri --version\nniri 25.01 (e35c630)\n```\n\nWhen building in a packaging system, there's usually no repository, so the commit hash is unavailable and the version will show \"unknown commit\".\nIn this case, please set the commit hash manually:\n\n```\n$ export NIRI_BUILD_COMMIT=\"e35c630\"\n...proceed to build niri\n```\n\nYou can also override the version string entirely, in this case please make sure the corresponding niri version stays intact:\n\n```\n$ export NIRI_BUILD_VERSION_STRING=\"25.01-1 (e35c630)\"\n...proceed to build niri\n```\n\nRemember to set this variable for both `cargo build` and `cargo install` since the latter will rebuild niri if the environment changes.\n\n### Panics\n\nGood panic backtraces are required for diagnosing niri crashes.\nPlease use the `niri panic` command to test that your package produces good backtraces.\n\n```\n$ niri panic\nthread 'main' panicked at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/core/src/time.rs:1142:31:\noverflow when subtracting durations\nstack backtrace:\n   0: rust_begin_unwind\n             at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/std/src/panicking.rs:665:5\n   1: core::panicking::panic_fmt\n             at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/core/src/panicking.rs:74:14\n   2: core::panicking::panic_display\n             at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/core/src/panicking.rs:264:5\n   3: core::option::expect_failed\n             at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/core/src/option.rs:2021:5\n   4: expect<core::time::Duration>\n             at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/core/src/option.rs:933:21\n   5: sub\n             at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/core/src/time.rs:1142:31\n   6: cause_panic\n             at /builddir/build/BUILD/niri-0.0.git.1699.279c8b6a-build/niri/src/utils/mod.rs:382:13\n   7: main\n             at /builddir/build/BUILD/niri-0.0.git.1699.279c8b6a-build/niri/src/main.rs:107:27\n   8: call_once<fn() -> core::result::Result<(), alloc::boxed::Box<dyn core::error::Error, alloc::alloc::Global>>, ()>\n             at /builddir/build/BUILD/rust-1.83.0-build/rustc-1.83.0-src/library/core/src/ops/function.rs:250:5\nnote: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n```\n\nImportant things to look for:\n\n- The panic message is there: \"overflow when subtracting durations\".\n- The backtrace goes all the way up to `main` and includes `cause_panic`.\n- The backtrace includes the file and line number for `cause_panic`: `at /.../src/utils/mod.rs:382:13`.\n\nIf possible, please ensure that your niri package on its own has good panics, i.e. *without* installing debuginfo or other packages.\nThe user likely won't have debuginfo installed when their compositor first crashes, and we really want to be able to diagnose and fix all crashes right away.\n\n### Rust dependencies\n\nEvery niri release comes with a vendored dependencies archive from `cargo vendor`.\nYou can use it to build the corresponding niri release completely offline.\n\nIf you don't want to use vendored dependencies, consider following the niri release's `Cargo.lock`.\nIt contains the exact dependency versions that I used when testing the release.\n\nIf you need to change the versions of some dependencies, pay extra attention to `smithay` and `smithay-drm-extras` commit hash.\nThese crates don't currently have regular stable releases, so niri uses git snapshots.\nUpstream frequently has breaking changes (API and behavior), so you're strongly advised to use the exact commit hash from the niri release's `Cargo.lock`.\n\n### Shell completions\n\nYou can generate shell completions for several shells via `niri completions <SHELL>`, i.e. `niri completions bash`.\nSee `niri completions -h` for a full list.\n"
  },
  {
    "path": "docs/wiki/README.md",
    "content": "Welcome to the niri documentation!\n\nFeel free to look through usage and [Getting started](./Getting-Started.md).\nIf you're looking for ways to configure niri, check out the [introduction to configuration](./Configuration:-Introduction.md).\n\nIf you'd like to help with niri, there are plenty of both coding- and non-coding-related ways to do so.\nSee [CONTRIBUTING.md](https://github.com/niri-wm/niri/blob/main/CONTRIBUTING.md) for an overview.\n\nIf you're not already here, check out our new wiki website! https://niri-wm.github.io/niri/\n\n---\n\nThe documentation is open to contribution, see [Documenting niri](./Development:-Documenting-niri.md).\nPlease discuss bigger changes in [our Matrix room](https://matrix.to/#/#niri:matrix.org) first!\nThe wiki is generated from files in the `docs/wiki/` folder of the repository, so you can open a pull request modifying it there.\n"
  },
  {
    "path": "docs/wiki/Screencasting.md",
    "content": "### Overview\n\nThe primary screencasting interface that niri offers is through portals and pipewire.\nIt is supported by [OBS], Firefox, Chromium, Electron, Telegram, and other apps.\nYou can screencast both monitors and individual windows.\n\nIn order to use it, you need a working D-Bus session, pipewire, `xdg-desktop-portal-gnome`, and [running niri as a session](./Getting-Started.md) (i.e. through `niri-session` or from a display manager).\nOn widely used distros this should all \"just work\".\n\nAlternatively, you can use tools that rely on the `wlr-screencopy` protocol, which niri also supports.\n\nThere are several features in niri designed for screencasting.\nLet's take a look!\n\n### Block out windows\n\nYou can block out specific windows from screencasts, replacing them with solid black rectangles.\nThis can be useful for password managers or messenger windows, etc.\n\n![Screenshot showing a window visible normally, but blocked out on OBS.](./img/block-out-from-screencast.png)\n\nThis is controlled through the `block-out-from` window rule, for example:\n\n```kdl\n// Block out password managers from screencasts.\nwindow-rule {\n    match app-id=r#\"^org\\.keepassxc\\.KeePassXC$\"#\n    match app-id=r#\"^org\\.gnome\\.World\\.Secrets$\"#\n\n    block-out-from \"screencast\"\n}\n```\n\nYou can similarly block out layer surfaces, using a layer rule:\n\n```kdl\n// Block out mako notifications from screencasts.\nlayer-rule {\n    match namespace=\"^notifications$\"\n\n    block-out-from \"screencast\"\n}\n```\n\nCheck [the corresponding wiki section](./Configuration:-Window-Rules.md#block-out-from) for more details and examples.\n\n### Dynamic screencast target\n\n<sup>Since: 25.05</sup>\n\nNiri provides a special screencast stream that you can change dynamically.\nIt shows up as \"niri Dynamic Cast Target\" in the screencast window dialog.\n\n![Screencast dialog showing niri Dynamic Cast Target.](https://github.com/user-attachments/assets/e236ce74-98ec-4f3a-a99b-29ac1ff324dd)\n\nWhen you select it, it will start as an empty, transparent video stream.\nThen, you can use the following binds to change what it shows:\n\n- `set-dynamic-cast-window` to cast the focused window.\n- `set-dynamic-cast-monitor` to cast the focused monitor.\n- `clear-dynamic-cast-target` to go back to an empty stream.\n\nYou can also use these actions from the command line, for example to interactively pick which window to cast:\n\n```sh\n$ niri msg action set-dynamic-cast-window --id $(niri msg --json pick-window | jq .id)\n```\n\n<video controls src=\"https://github.com/user-attachments/assets/c617a9d6-7d5e-4f1f-b8cc-9301182d9634\">\n\nhttps://github.com/user-attachments/assets/c617a9d6-7d5e-4f1f-b8cc-9301182d9634\n\n</video>\n\nIf the cast target disappears (e.g. the target window closes), the stream goes back to empty.\n\nAll 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).\n\n### Indicate screencasted windows\n\n<sup>Since: 25.02</sup>\n\nThe [`is-window-cast-target=true` window rule](./Configuration:-Window-Rules.md#is-window-cast-target) matches windows targeted by an ongoing window screencast.\nYou use it with a special border color to clearly indicate screencasted windows.\n\nThis also works for windows targeted by dynamic screencasts.\nHowever, it will not work for windows that just happen to be visible in a full-monitor screencast.\n\n```kdl\n// Indicate screencasted windows with red colors.\nwindow-rule {\n    match is-window-cast-target=true\n\n    focus-ring {\n        active-color \"#f38ba8\"\n        inactive-color \"#7d0d2d\"\n    }\n\n    border {\n        inactive-color \"#7d0d2d\"\n    }\n\n    shadow {\n        color \"#7d0d2d70\"\n    }\n\n    tab-indicator {\n        active-color \"#f38ba8\"\n        inactive-color \"#7d0d2d\"\n    }\n}\n```\n\nExample:\n\n![Screencasted window indicated with a red border and shadow.](https://github.com/user-attachments/assets/375b381e-3a87-4e94-8676-44404971d893)\n\n### Windowed (fake/detached) fullscreen\n\n<sup>Since: 25.05</sup>\n\nWhen screencasting browser-based presentations like Google Slides, you usually want to hide the browser UI, which requires making the browser fullscreen.\nThis is not always convenient, for example if you have an ultrawide monitor, or just want to leave the browser as a smaller window, without taking up an entire monitor.\n\nThe `toggle-windowed-fullscreen` bind helps with this.\nIt tells the app that it went fullscreen, while in reality leaving it as a normal window that you can resize and put wherever you want.\n\n```kdl\nbinds {\n    Mod+Ctrl+Shift+F { toggle-windowed-fullscreen; }\n}\n```\n\nKeep in mind that not all apps react to fullscreening, so it may sometimes look as if the bind did nothing.\n\nHere's an example showing a windowed-fullscreen Google Slides [presentation](https://youtu.be/Kmz8ODolnDg), along with the presenter view and a meeting app:\n\n![Windowed Google Slides presentation, another window showing the presenter view, and another window showing Zoom UI casting the presentation.](https://github.com/user-attachments/assets/b2b49eea-f5a0-4c0a-b537-51fd1949a59d)\n\n### Screen mirroring\n\nFor presentations it can be useful to mirror an output to another.\nCurrently, niri doesn't have built-in output mirroring, but you can use a third-party tool [`wl-mirror`](https://github.com/Ferdi265/wl-mirror) that mirrors an output to a window.\nNote that the command below requires [`jq`](https://jqlang.org/download/) to be installed.\n```kdl\nbinds {\n    Mod+P repeat=false { spawn-sh \"wl-mirror $(niri msg --json focused-output | jq -r .name)\"; }\n}\n```\nFocus the output you want to mirror, press <kbd>Mod</kbd><kbd>P</kbd> and move the `wl-mirror` window to the target output.\nFinally, fullscreen the `wl-mirror` window (by default, <kbd>Mod</kbd><kbd>Shift</kbd><kbd>F</kbd>).\n\n[OBS]: https://obsproject.com/\n"
  },
  {
    "path": "docs/wiki/Tabs.md",
    "content": "### Overview\n\n<sup>Since: 25.02</sup>\n\nYou can switch a column to present windows as tabs, rather than as vertical tiles.\nAll tabs in a column have the same window size, so this is useful to get more vertical space.\n\n![Terminal with a tab indicator on the left.](https://github.com/user-attachments/assets/0e94ac0d-796d-4f85-a264-c105ef41c13f)\n\nUse this bind to toggle a column between normal and tabbed display:\n\n```kdl\nbinds {\n   Mod+W { toggle-column-tabbed-display; }\n}\n```\n\nAll other binds remain the same: switch tabs with `focus-window-down/up`, add or remove windows with `consume-window-into-column`/`expel-window-from-column`, and so on.\n\nUnlike regular columns, tabbed columns can go full-screen with multiple windows.\n\n### Tab indicator\n\nTabbed columns show a tab indicator on the side.\nYou can click on the indicator to switch tabs.\n\nSee the [`tab-indicator` section in the layout section](./Configuration:-Layout.md#tab-indicator) to configure it.\n\nBy default, the indicator draws \"outside\" the column, so it can overlay other windows or go off-screen.\nThe `place-within-column` flag puts the indicator \"inside\" the column, adjusting the window size to make space for it.\nThis is especially useful for thicker tab indicators, or when you have very small gaps.\n\n| Default | `place-within-column` |\n| --- | --- |\n| ![A screenshot showing 4 windows, with the middle column being focused. The tab indicator overflows onto the left column](https://github.com/user-attachments/assets/c2f51f50-3d87-403a-8beb-cbbe5ec5c880) | ![A screenshot showing 4 windows, with the middle column being focused. The tab indicator is contained within its respective column](https://github.com/user-attachments/assets/f1797cd0-d518-4be6-95b4-3540523c4370) |\n"
  },
  {
    "path": "docs/wiki/Workspaces.md",
    "content": "### Overview\n\nNiri has dynamic workspaces that can move between monitors.\n\nEach monitor contains an independent set of workspaces arranged vertically.\nYou can switch between workspaces on a monitor with `focus-workspace-down` and `focus-workspace-up`.\nEmpty workspaces \"in the middle\" automatically disappear when you switch away from them.\n\nThere's always one empty workspace at the end (at the bottom) of every monitor.\nWhen you open a window on this empty workspace, a new empty workspace will immediately appear further below it.\n\nYou can move workspaces up and down on the monitor with `move-workspace-up/down`.\nThe way to put a window on a new workspace \"in the middle\" is to put it on the last (empty) workspace, then move the workspace up to where you need.\n\nHere's a visual representation that shows two monitors and their workspaces.\nThe left monitor has three workspaces (two with windows, plus one empty), and the right monitor has two workspaces (one with windows, plus one empty).\n\n<picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"./img/workspaces-dark.png\">\n    <img alt=\"Two monitors. First with three workspaces, second with two workspaces.\" src=\"./img/workspaces-light.png\">\n</picture>\n\nYou can move a workspace to a different monitor using binds like `move-workspace-to-monitor-left/right/up/down` and `move-workspace-to-monitor-next/previous`.\n\nWhen you disconnect a monitor, its workspaces will automatically move to a different monitor.\nBut, they will also \"remember\" their original monitor, so when you reconnect it, the workspaces will automatically move back to it.\n\n> [!TIP]\n> From other tiling WMs, you may be used to thinking about workspaces like this: \"These are all of my workspaces. I can show workspace X on my first monitor, and workspace Y on my second monitor.\"\n> In niri, instead, think like this: \"My first monitor contains these workspaces, including X and Y, and my second monitor contains these other workspaces. I can switch my first monitor to workspace X or Y. I can move workspace Y to my second monitor to show it there.\"\n\n### Addressing workspaces by index\n\nSeveral actions in niri can address workspaces \"by index\": `focus-workspace 2`, `move-column-to-workspace 4`.\nThis index refers to whichever workspace *currently happens to be* at this position on the focused monitor.\nSo, `focus-workspace 2` will always put you on the second workspace of the monitor, whichever workspace that currently is.\n\nThis is an important distinction from WMs with static workspace systems.\nIn niri, workspaces *do not have indices on their own*.\nIf you take the first workspace and move it further down on the monitor, `focus-workspace 1` will now put you on a different workspace (the one that was below the first workspace before you moved it).\n\nWhen you want to have a more permanent workspace in niri, you can create a [named workspace](./Configuration:-Named-Workspaces.md) in the config or via the `set-workspace-name` action.\nYou can refer to named workspaces by name, e.g. `focus-workspace \"browser\"`, and they won't disappear when they become empty.\n\n> [!TIP]\n> You can try to emulate static workspaces by creating workspaces named \"one\", \"two\", \"three\", ..., and binding keys to `focus-workspace \"one\"`, `focus-workspace \"two\"`, ...\n> This can work to some extent, but it can become somewhat confusing, since you can still move these workspaces up and down and between monitors.\n>\n> If you're coming from a static workspace WM, I suggest *not* doing that, but instead trying the \"niri way\" with dynamic workspaces, focusing and moving up/down instead of by index.\n> Thanks to scrollable tiling, you generally need fewer workspaces than on a traditional tiling WM.\n\n### Example workflow\n\nThis is how I like to use workspaces.\n\nI will usually have my browser on the topmost workspace, then one workspace per project (or a \"thing\") I'm working on.\nOn a single workspace I have 1–2 windows that fit inside a monitor that I switch between frequently, and maybe extra windows scrolled outside the view, usually either ones I need rarely, or temporary windows that I quickly close.\nWhen I need another permanent window, I'll put it on a new workspace.\n\nI actively move workspaces up and down as I'm working on things to make what I need accessible in one motion.\nFor example, I usually frequently switch between the browser and whatever I'm doing, so I always move whatever I'm currently doing to right below the browser, so a single `focus-workspace-up/down` gets me where I want.\n"
  },
  {
    "path": "docs/wiki/Xwayland.md",
    "content": "## Using xwayland-satellite\n\n<sup>Since: 25.08</sup> Niri integrates with [xwayland-satellite](https://github.com/Supreeeme/xwayland-satellite) out of the box.\nEnsure xwayland-satellite >= 0.7 is installed and available in `$PATH`.\nWith no further configuration, niri will create X11 sockets on disk, export `$DISPLAY`, and spawn xwayland-satellite on-demand when an X11 client connects.\nIf xwayland-satellite dies, niri will automatically restart it.\n\nIf you had a custom config which manually started `xwayland-satellite` and set `$DISPLAY`, you should remove those customizations for the automatic integration to work.\n\nTo check that the integration works, verify that the niri output says something like `listening on X11 socket: :0`:\n\n```sh\n$ journalctl --user-unit=niri -b\nsystemd[2338]: Starting niri.service - A scrollable-tiling Wayland compositor...\nniri[2474]: 2025-08-29T04:07:40.043402Z  INFO niri: starting version 25.05.1 (0.0.git.2345.d9833fc1)\n(...)\nniri[2474]: 2025-08-29T04:07:40.690512Z  INFO niri: listening on Wayland socket: wayland-1\nniri[2474]: 2025-08-29T04:07:40.690520Z  INFO niri: IPC listening on: /run/user/1000/niri.wayland-1.2474.sock\nniri[2474]: 2025-08-29T04:07:40.700137Z  INFO niri: listening on X11 socket: :0\nsystemd[2338]: Started niri.service - A scrollable-tiling Wayland compositor.\n$ echo $DISPLAY\n:0\n```\n\n![xwayland-satellite running Steam and Half-Life.](https://github.com/user-attachments/assets/57db8f96-40d4-4621-a389-373c169349a4)\n\nWe're using xwayland-satellite rather than Xwayland directly because [X11 is very cursed](./FAQ.md#why-doesnt-niri-integrate-xwayland-like-other-compositors).\nxwayland-satellite takes on the bulk of the work dealing with the X11 peculiarities from us, giving niri normal Wayland windows to manage.\n\nxwayland-satellite works well with most applications: Steam, games, Discord, even more exotic things like Ardour with wine Windows VST plugins.\nHowever, X11 apps that want to position windows or bars at specific screen coordinates won't behave correctly and will need a nested compositor to run.\nSee sections below for how to do that.\n\n## Using the labwc Wayland compositor\n\n[Labwc](https://github.com/labwc/labwc) is a traditional stacking Wayland compositor with Xwayland.\nYou can run it as a window, then run X11 apps inside.\n\n1. Install labwc from your distribution packages.\n1. Run it inside niri with the `labwc` command.\nIt will open as a new window.\n1. Run an X11 application on the X11 DISPLAY that it provides, e.g. `env DISPLAY=:0 glxgears`\n\n![Labwc running X11 apps.](https://github.com/user-attachments/assets/aecbcecb-f0cb-4909-867f-09d34b5a2d7e)\n\n## Directly running Xwayland in rootful mode\n\nThis method involves invoking XWayland directly and running it as its own window, it also requires an extra X11 window manager running inside it.\n\n![Xwayland running in rootful mode.](https://github.com/niri-wm/niri/assets/1794388/b64e96c4-a0bb-4316-94a0-ff445d4c7da7)\n\nHere's how you do it:\n\n1. Run `Xwayland` (just the binary on its own without flags).\nThis will spawn a black window which you can resize and fullscreen (with Mod+Shift+F) for convenience.\nOn older Xwayland versions the window will be screen-sized and non-resizable.\n1. Run some X11 window manager in there, e.g. `env DISPLAY=:0 i3`.\nThis way you can manage X11 windows inside the Xwayland instance.\n1. Run an X11 application there, e.g. `env DISPLAY=:0 flatpak run com.valvesoftware.Steam`.\n\nWith fullscreen game inside a fullscreen Xwayland you get pretty much a normal gaming experience.\n\n> [!TIP]\n> If you don't run an X11 window manager, Xwayland will close and re-open its window every time all X11 windows close and a new one opens.\n> To prevent this, start an X11 WM inside as mentioned above, or open some other long-running X11 window.\n\nOne caveat is that currently rootful Xwayland doesn't seem to share clipboard with the compositor.\nFor textual data you can do it manually using [wl-clipboard](https://github.com/bugaevc/wl-clipboard), for example:\n\n- `env DISPLAY=:0 xsel -ob | wl-copy` to copy from Xwayland to niri clipboard\n- `wl-paste -n | env DISPLAY=:0 xsel -ib` to copy from niri to Xwayland clipboard\n\nYou can also bind these to hotkeys if you want:\n\n```\nbinds {\n    Mod+Shift+C { spawn \"sh\" \"-c\" \"env DISPLAY=:0 xsel -ob | wl-copy\"; }\n    Mod+Shift+V { spawn \"sh\" \"-c\" \"wl-paste -n | env DISPLAY=:0 xsel -ib\"; }\n}\n```\n\n## Using xwayland-run to run Xwayland\n\n[xwayland-run] is a helper utility to run an X11 client within a dedicated Xwayland rootful server.\nIt takes care of starting Xwayland, setting the X11 DISPLAY environment variable, setting up xauth and running the specified X11 client using the newly started Xwayland instance.\nWhen the X11 client terminates, xwayland-run will automatically close the dedicated Xwayland server.\n\nIt works like this:\n\n```\nxwayland-run <Xwayland arguments> -- your-x11-app <X11 app arguments>\n```\n\nFor example:\n\n```\nxwayland-run -geometry 800x600 -fullscreen -- wine wingame.exe\n```\n\n## Using the Cage Wayland compositor\n\nIt is also possible to run the X11 application in [Cage](https://github.com/cage-kiosk/cage), which runs a nested Wayland session which also supports Xwayland, where the X11 application can run in.\n\nCompared to the Xwayland rootful method, this does not require running an extra X11 window manager, and can be used with one command `cage -- /path/to/application`. However, it can also cause issues if multiple windows are launched inside Cage, since Cage is meant to be used in kiosks, every new window will be automatically full-screened and take over the previously opened window.\n\nTo use Cage you need to:\n\n1. Install `cage`, it should be in most repositories.\n2. Run `cage -- /path/to/application` and enjoy your X11 program on niri.\n\nOptionally one can also modify the desktop entry for the application and add the `cage --` prefix to the `Exec` property. The Spotify Flatpak for example would look something like this:\n\n```ini\n[Desktop Entry]\nType=Application\nName=Spotify\nGenericName=Online music streaming service\nComment=Access all of your favorite music\nIcon=com.spotify.Client\nExec=cage -- flatpak run com.spotify.Client\nTerminal=false\n```\n\n## Proton-GE native Wayland\n\nIt's possible to run some games as native Wayland clients, sidestepping the issues related to X11. You can do it with a custom version of Proton like [Proton-GE](https://github.com/GloriousEggroll/proton-ge-custom) by setting the `PROTON_ENABLE_WAYLAND=1` environmental variable in the game's launch parameters. Do note that for now this is an experimental feature, might not work with every game and might have its own issues.\n\n```\nPROTON_ENABLE_WAYLAND=1 %command%\n```\n\n## Using gamescope\n\nYou can use [gamescope](https://github.com/ValveSoftware/gamescope) to run X11 games and even Steam itself.\n\nSimilar to Cage, gamescope will only show a single, topmost window, so it's not very suitable to running regular apps.\nBut you can run Steam in gamescope and then start some game from Steam just fine.\n\n```\ngamescope -- flatpak run com.valvesoftware.Steam\n```\n\nTo run gamescope fullscreen, you can pass flags that set the necessary resolution, and a flag that starts it in fullscreen mode:\n\n```\ngamescope -W 2560 -H 1440 -w 2560 -h 1440 -f  -- flatpak run com.valvesoftware.Steam\n```\n\n> [!NOTE]\n> If Steam terminates abnormally while running in gamescope, it seems that subsequent gamescope invocations will sometimes fail to start it properly.\n> If this happens, run Steam inside a rootful Xwayland as described above, then exit it normally, and then you will be able to use gamescope again.\n\n[xwayland-run]: https://gitlab.freedesktop.org/ofourdan/xwayland-run\n[xwayland-satellite]: https://github.com/Supreeeme/xwayland-satellite\n"
  },
  {
    "path": "docs/wiki/_Sidebar.md",
    "content": "## Usage\n* [Getting Started](./Getting-Started.md)\n* [Example systemd Setup](./Example-systemd-Setup.md)\n* [Important Software](./Important-Software.md)\n* [Workspaces](./Workspaces.md)\n* [Floating Windows](./Floating-Windows.md)\n* [Tabs](./Tabs.md)\n* [Overview](./Overview.md)\n* [Screencasting](./Screencasting.md)\n* [Layer‐Shell Components](./Layer%E2%80%90Shell-Components.md)\n* [IPC, `niri msg`](./IPC.md)\n* [Application-Specific Issues](./Application-Issues.md)\n* [Nvidia](./Nvidia.md)\n* [Xwayland](./Xwayland.md)\n* [Gestures](./Gestures.md)\n* [Fullscreen and Maximize](./Fullscreen-and-Maximize.md)\n* [Packaging niri](./Packaging-niri.md)\n* [Integrating niri](./Integrating-niri.md)\n* [Accessibility](./Accessibility.md)\n* [Name and Logo](./Name-and-Logo.md)\n* [FAQ](./FAQ.md)\n\n## Configuration\n* [Introduction](./Configuration:-Introduction.md)\n* [Input](./Configuration:-Input.md)\n* [Outputs](./Configuration:-Outputs.md)\n* [Key Bindings](./Configuration:-Key-Bindings.md)\n* [Switch Events](./Configuration:-Switch-Events.md)\n* [Layout](./Configuration:-Layout.md)\n* [Named Workspaces](./Configuration:-Named-Workspaces.md)\n* [Miscellaneous](./Configuration:-Miscellaneous.md)\n* [Window Rules](./Configuration:-Window-Rules.md)\n* [Layer Rules](./Configuration:-Layer-Rules.md)\n* [Animations](./Configuration:-Animations.md)\n* [Gestures](./Configuration:-Gestures.md)\n* [Recent Windows](./Configuration:-Recent-Windows.md)\n* [Debug Options](./Configuration:-Debug-Options.md)\n* [Include](./Configuration:-Include.md)\n\n## Development\n* [Design Principles](./Development:-Design-Principles.md)\n* [Developing niri](./Development:-Developing-niri.md)\n* [Documenting niri](./Development:-Documenting-niri.md)\n* [Fractional Layout](./Development:-Fractional-Layout.md)\n* [Redraw Loop](./Development:-Redraw-Loop.md)\n* [Animation Timing](./Development:-Animation-Timing.md)\n"
  },
  {
    "path": "docs/wiki/_assets/stylesheets/niri.css",
    "content": ":root  > * {\n  --md-primary-fg-color: #1e97b5;\n  --md-primary-fg-color--dark: color-mix(in srgb, var(--md-primary-fg-color), black 20%);\n  --md-accent-fg-color: var(--md-primary-fg-color--dark);\n}\n\n.md-logo > img {\n  background-color: white;\n  border-radius: 8px;\n}\n\n@media (prefers-color-scheme: dark) {\n  :root  > * {\n    --md-primary-fg-color: #167188;\n    --md-accent-fg-color: #1e97b5;\n    --md-typeset-a-color: color-mix(in srgb, var(--md-accent-fg-color), white 30%);\n  }\n\n  .md-logo > img {\n    background-color: #eee;\n  }\n}\n\n.md-nav--primary .md-nav__title {\n  box-shadow: none;\n}\n\n@media screen and (min-width: 76.25em) {\n  .md-nav__title {\n    display: none;\n  }\n}\n\n@media screen and (min-width: 76.25em) {\n  .md-main {\n    min-height: 100vh;\n  }\n}\n\n.md-nav__link--active {\n  font-weight: bold;\n}\n\n.highlight .gp, .highlight .go {\n  user-select: none;\n}\n\n.md-source-file__fact {\n  visibility: hidden;\n}\n\n.badge {\n    font-size: 0.85em;\n    box-shadow: 0 0 0 1px inset var(--md-accent-fg-color--transparent);\n    padding: 0.2rem 0.3rem;\n}\n\n.md-typeset table:not([class]) td {\n    padding: .5em 1.25em;\n    vertical-align: middle;\n}\n\n/* Improve keyboard shortcuts for screen readers: this way they won't break on them. */\n.md-typeset kbd {\n    display: inline;\n}\n"
  },
  {
    "path": "docs/wiki/examples/close_custom_shader.frag",
    "content": "// Your shader must contain one function (see the bottom of this file).\n//\n// It should not contain any uniform definitions or anything else, as niri\n// provides them for you.\n//\n// All symbols defined by niri will have a niri_ prefix, so don't use it for\n// your own variables and functions.\n\n// The function that you must define looks like this:\nvec4 close_color(vec3 coords_geo, vec3 size_geo) {\n    vec4 color = /* ...compute the color... */;\n    return color;\n}\n\n// It takes as input:\n//\n// * coords_geo: coordinates of the current pixel relative to the window\n//   geometry.\n//\n// These are homogeneous (the Z component is equal to 1) and scaled in such a\n// way that the 0 to 1 coordinates lie within the window geometry. Pixels\n// outside the window geometry will have coordinates below 0 or above 1.\n//\n// The window geometry is its \"visible bounds\" from the user's perspective.\n//\n// The shader runs over the full screen area, so you must expect and handle\n// coordinates outside the [0, 1] range. If the window is scrolled off-screen,\n// all of the coordinates to the shader can fall outside the [0, 1] range.\n//\n// * size_geo: size of the window geometry in logical pixels.\n//\n// It is homogeneous (the Z component is equal to 1).\n//\n// The function must return the color of the pixel (with premultiplied alpha).\n// The pixel color will be further processed by niri (for example, to apply the\n// final opacity from window rules).\n\n// Now let's go over the uniforms that niri defines.\n//\n// You should only rely on the uniforms documented here. Any other uniforms can\n// change or be removed without notice.\n\n// The window texture.\nuniform sampler2D niri_tex;\n\n// Matrix that converts geometry coordinates into the window texture\n// coordinates.\n//\n// The window texture can and will go outside the geometry (for client-side\n// decoration shadows for example), which is why this matrix is necessary.\nuniform mat3 niri_geo_to_tex;\n\n\n// Unclamped progress of the animation.\n//\n// Goes from 0 to 1 but may overshoot and oscillate.\nuniform float niri_progress;\n\n// Clamped progress of the animation.\n//\n// Goes from 0 to 1, but will stop at 1 as soon as it first reaches 1. Will not\n// overshoot or oscillate.\nuniform float niri_clamped_progress;\n\n// Random float in [0; 1), consistent for the duration of the animation.\nuniform float niri_random_seed;\n\n// Now let's look at some examples. You can copy everything below this line\n// into your custom-shader to experiment.\n\n// Example: fill the current geometry with a solid vertical gradient and\n// gradually make transparent.\nvec4 solid_gradient(vec3 coords_geo, vec3 size_geo) {\n    vec4 color = vec4(0.0);\n\n    // Paint only the area inside the current geometry.\n    if (0.0 <= coords_geo.x && coords_geo.x <= 1.0\n            && 0.0 <= coords_geo.y && coords_geo.y <= 1.0)\n    {\n        vec4 from = vec4(1.0, 0.0, 0.0, 1.0);\n        vec4 to = vec4(0.0, 1.0, 0.0, 1.0);\n        color = mix(from, to, coords_geo.y);\n    }\n\n    // Make it transparent.\n    color *= (1.0 - niri_clamped_progress);\n\n    return color;\n}\n\n// Example: gradually scale down and make transparent, equivalent to the\n// default closing animation.\nvec4 default_close(vec3 coords_geo, vec3 size_geo) {\n    // Scale down the window.\n    float scale = max(0.0, ((1.0 - niri_clamped_progress) / 5.0 + 0.8));\n    coords_geo = vec3((coords_geo.xy - vec2(0.5)) / scale + vec2(0.5), 1.0);\n\n    // Get color from the window texture.\n    vec3 coords_tex = niri_geo_to_tex * coords_geo;\n    vec4 color = texture2D(niri_tex, coords_tex.st);\n\n    // Make the window transparent.\n    color *= (1.0 - niri_clamped_progress);\n\n    return color;\n}\n\n// Example: make the window 'fall down' with slight rotation.\nvec4 fall_and_rotate(vec3 coords_geo, vec3 size_geo) {\n    // For this shader, set animation curve to linear for best results.\n\n    // Simulate an accelerated fall: square the (linear) progress.\n    float progress = niri_clamped_progress * niri_clamped_progress;\n\n    // Get our rotation pivot point coordinates at the bottom center of the window.\n    vec2 coords = (coords_geo.xy - vec2(0.5, 1.0)) * size_geo.xy;\n\n    // Move the window down to simulate a fall.\n    coords.y -= progress * 200.0;\n\n    // Randomize rotation direction and maximum angle.\n    float random = (niri_random_seed - 0.5) / 2.0;\n    random = sign(random) - random;\n    float max_angle = 0.05 * random;\n\n    // Rotate the window around our pivot point.\n    float angle = progress * max_angle;\n    mat2 rotate = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));\n    coords = rotate * coords;\n\n    // Transform the coordinates back.\n    coords_geo = vec3(coords / size_geo.xy + vec2(0.5, 1.0), 1.0);\n\n    // Sample the window texture.\n    vec3 coords_tex = niri_geo_to_tex * coords_geo;\n    vec4 color = texture2D(niri_tex, coords_tex.st);\n\n    // Multiply by alpha to fade out.\n    return color * (1.0 - niri_clamped_progress);\n}\n\n// This is the function that you must define.\nvec4 close_color(vec3 coords_geo, vec3 size_geo) {\n    // You can pick one of the example functions or write your own.\n    return fall_and_rotate(coords_geo, size_geo);\n}\n\n"
  },
  {
    "path": "docs/wiki/examples/open_custom_shader.frag",
    "content": "// Your shader must contain one function (see the bottom of this file).\n//\n// It should not contain any uniform definitions or anything else, as niri\n// provides them for you.\n//\n// All symbols defined by niri will have a niri_ prefix, so don't use it for\n// your own variables and functions.\n\n// The function that you must define looks like this:\nvec4 open_color(vec3 coords_geo, vec3 size_geo) {\n    vec4 color = /* ...compute the color... */;\n    return color;\n}\n\n// It takes as input:\n//\n// * coords_geo: coordinates of the current pixel relative to the window\n//   geometry.\n//\n// These are homogeneous (the Z component is equal to 1) and scaled in such a\n// way that the 0 to 1 coordinates lie within the window geometry. Pixels\n// outside the window geometry will have coordinates below 0 or above 1.\n//\n// The window geometry is its \"visible bounds\" from the user's perspective.\n//\n// The shader runs over an area of unspecified size and location, so you must\n// expect and handle coordinates outside the [0, 1] range. The area will be\n// larger than the final window size to accommodate more varied effects.\n//\n// * size_geo: size of the window geometry in logical pixels.\n//\n// It is homogeneous (the Z component is equal to 1).\n//\n// The function must return the color of the pixel (with premultiplied alpha).\n// The pixel color will be further processed by niri (for example, to apply the\n// final opacity from window rules).\n\n// Now let's go over the uniforms that niri defines.\n//\n// You should only rely on the uniforms documented here. Any other uniforms can\n// change or be removed without notice.\n\n// The window texture.\nuniform sampler2D niri_tex;\n\n// Matrix that converts geometry coordinates into the window texture\n// coordinates.\n//\n// The window texture can and will go outside the geometry (for client-side\n// decoration shadows for example), which is why this matrix is necessary.\nuniform mat3 niri_geo_to_tex;\n\n\n// Unclamped progress of the animation.\n//\n// Goes from 0 to 1 but may overshoot and oscillate.\nuniform float niri_progress;\n\n// Clamped progress of the animation.\n//\n// Goes from 0 to 1, but will stop at 1 as soon as it first reaches 1. Will not\n// overshoot or oscillate.\nuniform float niri_clamped_progress;\n\n// Random float in [0; 1), consistent for the duration of the animation.\nuniform float niri_random_seed;\n\n// Now let's look at some examples. You can copy everything below this line\n// into your custom-shader to experiment.\n\n// Example: fill the current geometry with a solid vertical gradient and\n// gradually make opaque.\nvec4 solid_gradient(vec3 coords_geo, vec3 size_geo) {\n    vec4 color = vec4(0.0);\n\n    // Paint only the area inside the current geometry.\n    if (0.0 <= coords_geo.x && coords_geo.x <= 1.0\n            && 0.0 <= coords_geo.y && coords_geo.y <= 1.0)\n    {\n        vec4 from = vec4(1.0, 0.0, 0.0, 1.0);\n        vec4 to = vec4(0.0, 1.0, 0.0, 1.0);\n        color = mix(from, to, coords_geo.y);\n    }\n\n    // Make it opaque.\n    color *= niri_clamped_progress;\n\n    return color;\n}\n\n// Example: gradually scale up and make opaque, equivalent to the default\n// opening animation.\nvec4 default_open(vec3 coords_geo, vec3 size_geo) {\n    // Scale up the window.\n    float scale = max(0.0, (niri_progress / 2.0 + 0.5));\n    coords_geo = vec3((coords_geo.xy - vec2(0.5)) / scale + vec2(0.5), 1.0);\n\n    // Get color from the window texture.\n    vec3 coords_tex = niri_geo_to_tex * coords_geo;\n    vec4 color = texture2D(niri_tex, coords_tex.st);\n\n    // Make the window opaque.\n    color *= niri_clamped_progress;\n\n    return color;\n}\n\n// Example: show the window as an expanding circle.\n// Recommended setting: duration-ms 250\nvec4 expanding_circle(vec3 coords_geo, vec3 size_geo) {\n    vec3 coords_tex = niri_geo_to_tex * coords_geo;\n    vec4 color = texture2D(niri_tex, coords_tex.st);\n\n    vec2 coords = (coords_geo.xy - vec2(0.5, 0.5)) * size_geo.xy * 2.0;\n    coords = coords / length(size_geo.xy);\n    float p = niri_clamped_progress;\n    if (p * p <= dot(coords, coords))\n        color = vec4(0.0);\n\n    return color;\n}\n\n// This is the function that you must define.\nvec4 open_color(vec3 coords_geo, vec3 size_geo) {\n    // You can pick one of the example functions or write your own.\n    return expanding_circle(coords_geo, size_geo);\n}\n\n"
  },
  {
    "path": "docs/wiki/examples/resize_custom_shader.frag",
    "content": "// Your shader must contain one function (see the bottom of this file).\n//\n// It should not contain any uniform definitions or anything else, as niri\n// provides them for you.\n//\n// All symbols defined by niri will have a niri_ prefix, so don't use it for\n// your own variables and functions.\n\n// The function that you must define looks like this:\nvec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    vec4 color = /* ...compute the color... */;\n    return color;\n}\n\n// It takes as input:\n//\n// * coords_curr_geo: coordinates of the current pixel relative to the current\n// window geometry.\n//\n// These are homogeneous (the Z component is equal to 1) and scaled in such a\n// way that the 0 to 1 coordinates lie within the current window geometry (in\n// the middle of a resize). Pixels outside the window geometry will have\n// coordinates below 0 or above 1.\n//\n// The window geometry is its \"visible bounds\" from the user's perspective.\n//\n// The shader runs over an area of unspecified size and location, so you must\n// expect and handle coordinates outside the [0, 1] range. The area will be\n// large enough to accommodate a crossfade effect.\n//\n// * size_curr_geo: size of the current window geometry in logical pixels.\n//\n// It is homogeneous (the Z component is equal to 1).\n//\n// The function must return the color of the pixel (with premultiplied alpha).\n// The pixel color will be further processed by niri (for example, to apply the\n// final opacity from window rules).\n\n// Now let's go over the uniforms that niri defines.\n//\n// You should only rely on the uniforms documented here. Any other uniforms can\n// change or be removed without notice.\n\n// Previous (before resize) window texture.\nuniform sampler2D niri_tex_prev;\n\n// Matrix that converts geometry coordinates into the previous window texture\n// coordinates.\n//\n// The window texture can and will go outside the geometry (for client-side\n// decoration shadows for example), which is why this matrix is necessary.\nuniform mat3 niri_geo_to_tex_prev;\n\n// Next (after resize) window texture.\nuniform sampler2D niri_tex_next;\n\n// Matrix that converts geometry coordinates into the next window texture\n// coordinates.\nuniform mat3 niri_geo_to_tex_next;\n\n\n// Matrix that converts coords_curr_geo into coordinates inside the previous\n// (before resize) window geometry.\nuniform mat3 niri_curr_geo_to_prev_geo;\n\n// Matrix that converts coords_curr_geo into coordinates inside the next\n// (after resize) window geometry.\nuniform mat3 niri_curr_geo_to_next_geo;\n\n\n// Unclamped progress of the animation.\n//\n// Goes from 0 to 1 but may overshoot and oscillate.\nuniform float niri_progress;\n\n// Clamped progress of the animation.\n//\n// Goes from 0 to 1, but will stop at 1 as soon as it first reaches 1. Will not\n// overshoot or oscillate.\nuniform float niri_clamped_progress;\n\n// Now let's look at some examples. You can copy everything below this line\n// into your custom-shader to experiment.\n\n// Example: fill the current geometry with a solid vertical gradient.\nvec4 solid_gradient(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    vec3 coords = coords_curr_geo;\n    vec4 color = vec4(0.0);\n\n    // Paint only the area inside the current geometry.\n    if (0.0 <= coords.x && coords.x <= 1.0\n            && 0.0 <= coords.y && coords.y <= 1.0)\n    {\n        vec4 from = vec4(1.0, 0.0, 0.0, 1.0);\n        vec4 to = vec4(0.0, 1.0, 0.0, 1.0);\n        color = mix(from, to, coords.y);\n    }\n\n    return color;\n}\n\n// Example: crossfade between previous and next texture, stretched to the\n// current geometry.\nvec4 crossfade(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    // Convert coordinates into the texture space for sampling.\n    vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo;\n    vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st);\n\n    // Convert coordinates into the texture space for sampling.\n    vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo;\n    vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st);\n\n    vec4 color = mix(color_prev, color_next, niri_clamped_progress);\n    return color;\n}\n\n// Example: next texture, stretched to the current geometry.\nvec4 stretch_next(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo;\n    vec4 color = texture2D(niri_tex_next, coords_tex_next.st);\n    return color;\n}\n\n// Example: next texture, stretched to the current geometry if smaller, and\n// cropped if bigger.\nvec4 stretch_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo;\n\n    vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo;\n    vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo;\n\n    // We can crop if the current window size is smaller than the next window\n    // size. One way to tell is by comparing to 1.0 the X and Y scaling\n    // coefficients in the current-to-next transformation matrix.\n    bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0;\n    bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0;\n\n    vec3 coords = coords_stretch;\n    if (can_crop_by_x)\n        coords.x = coords_crop.x;\n    if (can_crop_by_y)\n        coords.y = coords_crop.y;\n\n    vec4 color = texture2D(niri_tex_next, coords.st);\n\n    // However, when we crop, we also want to crop out anything outside the\n    // current geometry. This is because the area of the shader is unspecified\n    // and usually bigger than the current geometry, so if we don't fill pixels\n    // outside with transparency, the texture will leak out.\n    //\n    // When stretching, this is not an issue because the area outside will\n    // correspond to client-side decoration shadows, which are already supposed\n    // to be outside.\n    if (can_crop_by_x && (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x))\n        color = vec4(0.0);\n    if (can_crop_by_y && (coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y))\n        color = vec4(0.0);\n\n    return color;\n}\n\n// Example: cropped next texture if it's bigger than the current geometry, and\n// crossfade between previous and next texture otherwise.\nvec4 crossfade_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo;\n    vec3 coords_prev_geo = niri_curr_geo_to_prev_geo * coords_curr_geo;\n\n    vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo;\n    vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo;\n    vec3 coords_stretch_prev = niri_geo_to_tex_prev * coords_curr_geo;\n\n    // We can crop if the current window size is smaller than the next window\n    // size. One way to tell is by comparing to 1.0 the X and Y scaling\n    // coefficients in the current-to-next transformation matrix.\n    bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0;\n    bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0;\n    bool crop = can_crop_by_x && can_crop_by_y;\n\n    vec4 color;\n\n    if (crop) {\n        // However, when we crop, we also want to crop out anything outside the\n        // current geometry. This is because the area of the shader is unspecified\n        // and usually bigger than the current geometry, so if we don't fill pixels\n        // outside with transparency, the texture will leak out.\n        //\n        // When crossfading, this is not an issue because the area outside will\n        // correspond to client-side decoration shadows, which are already supposed\n        // to be outside.\n        if (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x ||\n                coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y) {\n            color = vec4(0.0);\n        } else {\n            color = texture2D(niri_tex_next, coords_crop.st);\n        }\n    } else {\n        // If we can't crop, then crossfade.\n        color = texture2D(niri_tex_next, coords_stretch.st);\n        vec4 color_prev = texture2D(niri_tex_prev, coords_stretch_prev.st);\n        color = mix(color_prev, color, niri_clamped_progress);\n    }\n\n    return color;\n}\n\n// This is the function that you must define.\nvec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    // You can pick one of the example functions or write your own.\n    return stretch_or_crop_next(coords_curr_geo, size_curr_geo);\n}\n\n"
  },
  {
    "path": "flake.nix",
    "content": "# This flake file is community maintained\n{\n  description = \"Niri: A scrollable-tiling Wayland compositor.\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixpkgs-unstable\";\n\n    # NOTE: This is not necessary for end users\n    # You can omit it with `inputs.rust-overlay.follows = \"\"`\n    rust-overlay = {\n      url = \"github:oxalica/rust-overlay\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n  };\n\n  outputs =\n    {\n      self,\n      nixpkgs,\n      rust-overlay,\n    }:\n    let\n      revision = self.shortRev or self.dirtyShortRev or \"unknown\";\n      niri-package =\n        {\n          lib,\n          cairo,\n          dbus,\n          libGL,\n          libdisplay-info,\n          libinput,\n          seatd,\n          libxkbcommon,\n          libgbm,\n          pango,\n          pipewire,\n          pkg-config,\n          rustPlatform,\n          systemd,\n          wayland,\n          installShellFiles,\n          withDbus ? true,\n          withSystemd ? true,\n          withScreencastSupport ? true,\n          withDinit ? false,\n        }:\n\n        rustPlatform.buildRustPackage {\n          pname = \"niri\";\n          version = revision;\n\n          src = lib.fileset.toSource {\n            root = ./.;\n            fileset = lib.fileset.unions [\n              ./niri-config\n              ./niri-ipc\n              ./niri-visual-tests\n              ./resources\n              ./src\n              ./Cargo.toml\n              ./Cargo.lock\n            ];\n          };\n\n          postPatch = ''\n            patchShebangs resources/niri-session\n            substituteInPlace resources/niri.service \\\n              --replace-fail 'ExecStart=niri' \"ExecStart=$out/bin/niri\"\n          '';\n\n          cargoLock = {\n            # NOTE: This is only used for Git dependencies\n            allowBuiltinFetchGit = true;\n            lockFile = ./Cargo.lock;\n          };\n\n          strictDeps = true;\n\n          nativeBuildInputs = [\n            rustPlatform.bindgenHook\n            pkg-config\n            installShellFiles\n          ];\n\n          buildInputs =\n            [\n              cairo\n              dbus\n              libGL\n              libdisplay-info\n              libinput\n              seatd\n              libxkbcommon\n              libgbm\n              pango\n              wayland\n            ]\n            ++ lib.optional (withDbus || withScreencastSupport || withSystemd) dbus\n            ++ lib.optional withScreencastSupport pipewire\n            # Also includes libudev\n            ++ lib.optional withSystemd systemd;\n\n          buildFeatures =\n            lib.optional withDbus \"dbus\"\n            ++ lib.optional withDinit \"dinit\"\n            ++ lib.optional withScreencastSupport \"xdp-gnome-screencast\"\n            ++ lib.optional withSystemd \"systemd\";\n          buildNoDefaultFeatures = true;\n\n          # ever since this commit:\n          # https://github.com/niri-wm/niri/commit/771ea1e81557ffe7af9cbdbec161601575b64d81\n          # niri now runs an actual instance of the real compositor (with a mock backend) during tests\n          # and thus creates a real socket file in the runtime dir.\n          # this is fine for our build, we just need to make sure it has a directory to write to.\n          preCheck = ''\n            export XDG_RUNTIME_DIR=\"$(mktemp -d)\"\n          '';\n\n          checkFlags = [\n            # These tests require the ability to access a \"valid EGL Display\", but that won't work\n            # inside the Nix sandbox\n            \"--skip=::egl\"\n          ];\n\n          postInstall =\n            ''\n              installShellCompletion --cmd niri \\\n                --bash <($out/bin/niri completions bash) \\\n                --fish <($out/bin/niri completions fish) \\\n                --nushell <($out/bin/niri completions nushell) \\\n                --zsh <($out/bin/niri completions zsh)\n\n              install -Dm644 resources/niri.desktop -t $out/share/wayland-sessions\n              install -Dm644 resources/niri-portals.conf -t $out/share/xdg-desktop-portal\n            ''\n            + lib.optionalString withSystemd ''\n              install -Dm755 resources/niri-session $out/bin/niri-session\n              install -Dm644 resources/niri{.service,-shutdown.target} -t $out/share/systemd/user\n            '';\n\n          env = {\n            # Force linking with libEGL and libwayland-client\n            # so they can be discovered by `dlopen()`\n            RUSTFLAGS = toString (\n              map (arg: \"-C link-arg=\" + arg) [\n                \"-Wl,--push-state,--no-as-needed\"\n                \"-lEGL\"\n                \"-lwayland-client\"\n                \"-Wl,--pop-state\"\n              ]\n            );\n            NIRI_BUILD_COMMIT = revision;\n          };\n\n          passthru = {\n            providedSessions = [ \"niri\" ];\n          };\n\n          meta = {\n            description = \"Scrollable-tiling Wayland compositor\";\n            homepage = \"https://github.com/niri-wm/niri\";\n            license = lib.licenses.gpl3Only;\n            mainProgram = \"niri\";\n            platforms = lib.platforms.linux;\n          };\n        };\n\n      inherit (nixpkgs) lib;\n      # Support all Linux systems that the nixpkgs flake exposes\n      systems = lib.intersectLists lib.systems.flakeExposed lib.platforms.linux;\n\n      forAllSystems = lib.genAttrs systems;\n      nixpkgsFor = forAllSystems (system: nixpkgs.legacyPackages.${system});\n    in\n    {\n      checks = forAllSystems (system: {\n        # We use the debug build here to save a bit of time\n        inherit (self.packages.${system}) niri-debug;\n      });\n\n      devShells = forAllSystems (\n        system:\n        let\n          pkgs = nixpkgsFor.${system};\n          rust-bin = rust-overlay.lib.mkRustBin { } pkgs;\n          inherit (self.packages.${system}) niri;\n        in\n        {\n          default = pkgs.mkShell {\n            packages = [\n              # We don't use the toolchain from nixpkgs\n              # because we prefer a nightly toolchain\n              # and we *require* a nightly rustfmt\n              (rust-bin.selectLatestNightlyWith (\n                toolchain:\n                toolchain.default.override {\n                  extensions = [\n                    # includes already:\n                    # rustc\n                    # cargo\n                    # rust-std\n                    # rust-docs\n                    # rustfmt-preview\n                    # clippy-preview\n                    \"rust-analyzer\"\n                    \"rust-src\"\n                  ];\n                }\n              ))\n              pkgs.cargo-insta\n            ];\n\n            nativeBuildInputs = [\n              pkgs.rustPlatform.bindgenHook\n              pkgs.pkg-config\n              pkgs.wrapGAppsHook4 # For `niri-visual-tests`\n            ];\n\n            buildInputs = niri.buildInputs ++ [\n              pkgs.libadwaita # For `niri-visual-tests`\n            ];\n\n            env = {\n              # WARN: Do not overwrite this variable in your shell!\n              # It is required for `dlopen()` to work on some libraries; see the comment\n              # in the package expression\n              #\n              # This should only be set with `CARGO_BUILD_RUSTFLAGS=\"$CARGO_BUILD_RUSTFLAGS -C your-flags\"`\n              CARGO_BUILD_RUSTFLAGS = niri.RUSTFLAGS;\n            };\n          };\n        }\n      );\n\n      formatter = forAllSystems (system: nixpkgsFor.${system}.nixfmt-rfc-style);\n\n      packages = forAllSystems (\n        system:\n        let\n          niri = nixpkgsFor.${system}.callPackage niri-package { };\n        in\n        {\n          inherit niri;\n\n          # NOTE: This is for development purposes only\n          #\n          # It is primarily to help with quickly iterating on\n          # changes made to the above expression - though it is\n          # also not stripped in order to better debug niri itself\n          niri-debug = niri.overrideAttrs (\n            newAttrs: oldAttrs: {\n              pname = oldAttrs.pname + \"-debug\";\n\n              cargoBuildType = \"debug\";\n              cargoCheckType = newAttrs.cargoBuildType;\n\n              dontStrip = true;\n            }\n          );\n\n          default = niri;\n        }\n      );\n\n      overlays.default = final: _: {\n        niri = final.callPackage niri-package { };\n      };\n    };\n}\n"
  },
  {
    "path": "niri-config/Cargo.toml",
    "content": "[package]\nname = \"niri-config\"\nversion.workspace = true\ndescription.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrepository.workspace = true\n\n[dependencies]\nbitflags.workspace = true\ncsscolorparser = \"0.8.3\"\nknuffel = \"3.2.0\"\nmiette = { version = \"5.10.0\", features = [\"fancy-no-backtrace\"] }\nniri-ipc = { version = \"25.11.0\", path = \"../niri-ipc\" }\nregex = \"1.12.3\"\nsmithay = { workspace = true, features = [\"backend_libinput\"] }\ntracing.workspace = true\ntracy-client.workspace = true\n\n[dev-dependencies]\ninsta.workspace = true\npretty_assertions = \"1.4.1\"\ndiff = \"0.1.13\"\n"
  },
  {
    "path": "niri-config/src/animations.rs",
    "content": "use knuffel::errors::DecodeError;\nuse knuffel::Decode as _;\n\nuse crate::utils::{expect_only_children, parse_arg_node, MergeWith};\nuse crate::FloatOrInt;\n\n#[derive(Debug, Clone, PartialEq)]\npub struct Animations {\n    pub off: bool,\n    pub slowdown: f64,\n    pub workspace_switch: WorkspaceSwitchAnim,\n    pub window_open: WindowOpenAnim,\n    pub window_close: WindowCloseAnim,\n    pub horizontal_view_movement: HorizontalViewMovementAnim,\n    pub window_movement: WindowMovementAnim,\n    pub window_resize: WindowResizeAnim,\n    pub config_notification_open_close: ConfigNotificationOpenCloseAnim,\n    pub exit_confirmation_open_close: ExitConfirmationOpenCloseAnim,\n    pub screenshot_ui_open: ScreenshotUiOpenAnim,\n    pub overview_open_close: OverviewOpenCloseAnim,\n    pub recent_windows_close: RecentWindowsCloseAnim,\n}\n\nimpl Default for Animations {\n    fn default() -> Self {\n        Self {\n            off: false,\n            slowdown: 1.,\n            workspace_switch: Default::default(),\n            horizontal_view_movement: Default::default(),\n            window_movement: Default::default(),\n            window_open: Default::default(),\n            window_close: Default::default(),\n            window_resize: Default::default(),\n            config_notification_open_close: Default::default(),\n            exit_confirmation_open_close: Default::default(),\n            screenshot_ui_open: Default::default(),\n            overview_open_close: Default::default(),\n            recent_windows_close: Default::default(),\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq)]\npub struct AnimationsPart {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child, unwrap(argument))]\n    pub slowdown: Option<FloatOrInt<0, { i32::MAX }>>,\n    #[knuffel(child)]\n    pub workspace_switch: Option<WorkspaceSwitchAnim>,\n    #[knuffel(child)]\n    pub window_open: Option<WindowOpenAnim>,\n    #[knuffel(child)]\n    pub window_close: Option<WindowCloseAnim>,\n    #[knuffel(child)]\n    pub horizontal_view_movement: Option<HorizontalViewMovementAnim>,\n    #[knuffel(child)]\n    pub window_movement: Option<WindowMovementAnim>,\n    #[knuffel(child)]\n    pub window_resize: Option<WindowResizeAnim>,\n    #[knuffel(child)]\n    pub config_notification_open_close: Option<ConfigNotificationOpenCloseAnim>,\n    #[knuffel(child)]\n    pub exit_confirmation_open_close: Option<ExitConfirmationOpenCloseAnim>,\n    #[knuffel(child)]\n    pub screenshot_ui_open: Option<ScreenshotUiOpenAnim>,\n    #[knuffel(child)]\n    pub overview_open_close: Option<OverviewOpenCloseAnim>,\n    #[knuffel(child)]\n    pub recent_windows_close: Option<RecentWindowsCloseAnim>,\n}\n\nimpl MergeWith<AnimationsPart> for Animations {\n    fn merge_with(&mut self, part: &AnimationsPart) {\n        self.off |= part.off;\n        if part.on {\n            self.off = false;\n        }\n\n        merge!((self, part), slowdown);\n\n        // Animation properties are fairly tied together, except maybe `off`. So let's just save\n        // ourselves the work and not merge within individual animations.\n        merge_clone!(\n            (self, part),\n            workspace_switch,\n            window_open,\n            window_close,\n            horizontal_view_movement,\n            window_movement,\n            window_resize,\n            config_notification_open_close,\n            exit_confirmation_open_close,\n            screenshot_ui_open,\n            overview_open_close,\n            recent_windows_close,\n        );\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Animation {\n    pub off: bool,\n    pub kind: Kind,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum Kind {\n    Easing(EasingParams),\n    Spring(SpringParams),\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct EasingParams {\n    pub duration_ms: u32,\n    pub curve: Curve,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum Curve {\n    Linear,\n    EaseOutQuad,\n    EaseOutCubic,\n    EaseOutExpo,\n    CubicBezier(f64, f64, f64, f64),\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct SpringParams {\n    pub damping_ratio: f64,\n    pub stiffness: u32,\n    pub epsilon: f64,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct WorkspaceSwitchAnim(pub Animation);\n\nimpl Default for WorkspaceSwitchAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Spring(SpringParams {\n                damping_ratio: 1.,\n                stiffness: 1000,\n                epsilon: 0.0001,\n            }),\n        })\n    }\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct WindowOpenAnim {\n    pub anim: Animation,\n    pub custom_shader: Option<String>,\n}\n\nimpl Default for WindowOpenAnim {\n    fn default() -> Self {\n        Self {\n            anim: Animation {\n                off: false,\n                kind: Kind::Easing(EasingParams {\n                    duration_ms: 150,\n                    curve: Curve::EaseOutExpo,\n                }),\n            },\n            custom_shader: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct WindowCloseAnim {\n    pub anim: Animation,\n    pub custom_shader: Option<String>,\n}\n\nimpl Default for WindowCloseAnim {\n    fn default() -> Self {\n        Self {\n            anim: Animation {\n                off: false,\n                kind: Kind::Easing(EasingParams {\n                    duration_ms: 150,\n                    curve: Curve::EaseOutQuad,\n                }),\n            },\n            custom_shader: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct HorizontalViewMovementAnim(pub Animation);\n\nimpl Default for HorizontalViewMovementAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Spring(SpringParams {\n                damping_ratio: 1.,\n                stiffness: 800,\n                epsilon: 0.0001,\n            }),\n        })\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct WindowMovementAnim(pub Animation);\n\nimpl Default for WindowMovementAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Spring(SpringParams {\n                damping_ratio: 1.,\n                stiffness: 800,\n                epsilon: 0.0001,\n            }),\n        })\n    }\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct WindowResizeAnim {\n    pub anim: Animation,\n    pub custom_shader: Option<String>,\n}\n\nimpl Default for WindowResizeAnim {\n    fn default() -> Self {\n        Self {\n            anim: Animation {\n                off: false,\n                kind: Kind::Spring(SpringParams {\n                    damping_ratio: 1.,\n                    stiffness: 800,\n                    epsilon: 0.0001,\n                }),\n            },\n            custom_shader: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct ConfigNotificationOpenCloseAnim(pub Animation);\n\nimpl Default for ConfigNotificationOpenCloseAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Spring(SpringParams {\n                damping_ratio: 0.6,\n                stiffness: 1000,\n                epsilon: 0.001,\n            }),\n        })\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct ExitConfirmationOpenCloseAnim(pub Animation);\n\nimpl Default for ExitConfirmationOpenCloseAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Spring(SpringParams {\n                damping_ratio: 0.6,\n                stiffness: 500,\n                epsilon: 0.01,\n            }),\n        })\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct ScreenshotUiOpenAnim(pub Animation);\n\nimpl Default for ScreenshotUiOpenAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Easing(EasingParams {\n                duration_ms: 200,\n                curve: Curve::EaseOutQuad,\n            }),\n        })\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct OverviewOpenCloseAnim(pub Animation);\n\nimpl Default for OverviewOpenCloseAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Spring(SpringParams {\n                damping_ratio: 1.,\n                stiffness: 800,\n                epsilon: 0.0001,\n            }),\n        })\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct RecentWindowsCloseAnim(pub Animation);\n\nimpl Default for RecentWindowsCloseAnim {\n    fn default() -> Self {\n        Self(Animation {\n            off: false,\n            kind: Kind::Spring(SpringParams {\n                damping_ratio: 1.,\n                stiffness: 800,\n                epsilon: 0.001,\n            }),\n        })\n    }\n}\n\nimpl<S> knuffel::Decode<S> for WorkspaceSwitchAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for HorizontalViewMovementAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for WindowMovementAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for WindowOpenAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().anim;\n        let mut custom_shader = None;\n        let anim = Animation::decode_node(node, ctx, default, |child, ctx| {\n            if &**child.node_name == \"custom-shader\" {\n                custom_shader = parse_arg_node(\"custom-shader\", child, ctx)?;\n                Ok(true)\n            } else {\n                Ok(false)\n            }\n        })?;\n\n        Ok(Self {\n            anim,\n            custom_shader,\n        })\n    }\n}\n\nimpl<S> knuffel::Decode<S> for WindowCloseAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().anim;\n        let mut custom_shader = None;\n        let anim = Animation::decode_node(node, ctx, default, |child, ctx| {\n            if &**child.node_name == \"custom-shader\" {\n                custom_shader = parse_arg_node(\"custom-shader\", child, ctx)?;\n                Ok(true)\n            } else {\n                Ok(false)\n            }\n        })?;\n\n        Ok(Self {\n            anim,\n            custom_shader,\n        })\n    }\n}\n\nimpl<S> knuffel::Decode<S> for WindowResizeAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().anim;\n        let mut custom_shader = None;\n        let anim = Animation::decode_node(node, ctx, default, |child, ctx| {\n            if &**child.node_name == \"custom-shader\" {\n                custom_shader = parse_arg_node(\"custom-shader\", child, ctx)?;\n                Ok(true)\n            } else {\n                Ok(false)\n            }\n        })?;\n\n        Ok(Self {\n            anim,\n            custom_shader,\n        })\n    }\n}\n\nimpl<S> knuffel::Decode<S> for ConfigNotificationOpenCloseAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for ExitConfirmationOpenCloseAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for ScreenshotUiOpenAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for OverviewOpenCloseAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for RecentWindowsCloseAnim\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let default = Self::default().0;\n        Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {\n            Ok(false)\n        })?))\n    }\n}\n\nimpl Animation {\n    pub fn new_off() -> Self {\n        Self {\n            off: true,\n            kind: Kind::Easing(EasingParams {\n                duration_ms: 0,\n                curve: Curve::Linear,\n            }),\n        }\n    }\n\n    fn decode_node<S: knuffel::traits::ErrorSpan>(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n        default: Self,\n        mut process_children: impl FnMut(\n            &knuffel::ast::SpannedNode<S>,\n            &mut knuffel::decode::Context<S>,\n        ) -> Result<bool, DecodeError<S>>,\n    ) -> Result<Self, DecodeError<S>> {\n        #[derive(Default, PartialEq)]\n        struct OptionalEasingParams {\n            duration_ms: Option<u32>,\n            curve: Option<Curve>,\n        }\n\n        expect_only_children(node, ctx);\n\n        let mut off = false;\n        let mut easing_params = OptionalEasingParams::default();\n        let mut spring_params = None;\n\n        for child in node.children() {\n            match &**child.node_name {\n                \"off\" => {\n                    knuffel::decode::check_flag_node(child, ctx);\n                    if off {\n                        ctx.emit_error(DecodeError::unexpected(\n                            &child.node_name,\n                            \"node\",\n                            \"duplicate node `off`, single node expected\",\n                        ));\n                    } else {\n                        off = true;\n                    }\n                }\n                \"spring\" => {\n                    if easing_params != OptionalEasingParams::default() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            child,\n                            \"node\",\n                            \"cannot set both spring and easing parameters at once\",\n                        ));\n                    }\n                    if spring_params.is_some() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            &child.node_name,\n                            \"node\",\n                            \"duplicate node `spring`, single node expected\",\n                        ));\n                    }\n\n                    spring_params = Some(SpringParams::decode_node(child, ctx)?);\n                }\n                \"duration-ms\" => {\n                    if spring_params.is_some() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            child,\n                            \"node\",\n                            \"cannot set both spring and easing parameters at once\",\n                        ));\n                    }\n                    if easing_params.duration_ms.is_some() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            &child.node_name,\n                            \"node\",\n                            \"duplicate node `duration-ms`, single node expected\",\n                        ));\n                    }\n\n                    easing_params.duration_ms = Some(parse_arg_node(\"duration-ms\", child, ctx)?);\n                }\n                \"curve\" => {\n                    if spring_params.is_some() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            child,\n                            \"node\",\n                            \"cannot set both spring and easing parameters at once\",\n                        ));\n                    }\n                    if easing_params.curve.is_some() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            &child.node_name,\n                            \"node\",\n                            \"duplicate node `curve`, single node expected\",\n                        ));\n                    }\n\n                    let mut iter_args = child.arguments.iter();\n                    let val = iter_args.next().ok_or_else(|| {\n                        DecodeError::missing(child, \"additional argument `curve` is required\")\n                    })?;\n                    let animation_curve_string: String =\n                        knuffel::traits::DecodeScalar::decode(val, ctx)?;\n\n                    let animation_curve = match animation_curve_string.as_str() {\n                        \"linear\" => Some(Curve::Linear),\n                        \"ease-out-quad\" => Some(Curve::EaseOutQuad),\n                        \"ease-out-cubic\" => Some(Curve::EaseOutCubic),\n                        \"ease-out-expo\" => Some(Curve::EaseOutExpo),\n                        \"cubic-bezier\" => {\n                            let val = iter_args.next().ok_or_else(|| {\n                                DecodeError::missing(\n                                    child,\n                                    \"missing x1 coordinate for cubic Bézier curve control point\",\n                                )\n                            })?;\n                            // the X axis represents time frame so it cannot be negative\n                            // or larger than 1\n                            let x1: FloatOrInt<0, 1> =\n                                knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                            let val = iter_args.next().ok_or_else(|| {\n                                DecodeError::missing(\n                                    child,\n                                    \"missing y1 coordinate for cubic Bézier curve control point\",\n                                )\n                            })?;\n                            let y1: FloatOrInt<{ i32::MIN }, { i32::MAX }> =\n                                knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                            let val = iter_args.next().ok_or_else(|| {\n                                DecodeError::missing(\n                                    child,\n                                    \"missing x2 coordinate for cubic Bézier curve control point\",\n                                )\n                            })?;\n                            let x2: FloatOrInt<0, 1> =\n                                knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                            let val = iter_args.next().ok_or_else(|| {\n                                DecodeError::missing(\n                                    child,\n                                    \"missing y2 coordinate for cubic Bézier curve control point\",\n                                )\n                            })?;\n                            let y2: FloatOrInt<{ i32::MIN }, { i32::MAX }> =\n                                knuffel::traits::DecodeScalar::decode(val, ctx)?;\n\n                            Some(Curve::CubicBezier(x1.0, y1.0, x2.0, y2.0))\n                        }\n                        unexpected_curve => {\n                            ctx.emit_error(DecodeError::unexpected(\n                                &val.literal,\n                                \"argument\",\n                                format!(\n                                    \"unexpected animation curve `{unexpected_curve}`. \\\n                                    Niri only supports five animation curves: \\\n                                    `ease-out-quad`, `ease-out-cubic`, `ease-out-expo`, `linear` and `cubic-bezier`.\"\n                                ),\n                            ));\n\n                            None\n                        }\n                    };\n\n                    if let Some(val) = iter_args.next() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            &val.literal,\n                            \"argument\",\n                            \"unexpected argument\",\n                        ));\n                    }\n                    for name in child.properties.keys() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            name,\n                            \"property\",\n                            format!(\"unexpected property `{}`\", name.escape_default()),\n                        ));\n                    }\n                    for child in child.children() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            child,\n                            \"node\",\n                            format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n                        ));\n                    }\n\n                    easing_params.curve = animation_curve;\n                }\n                name_str => {\n                    if !process_children(child, ctx)? {\n                        ctx.emit_error(DecodeError::unexpected(\n                            child,\n                            \"node\",\n                            format!(\"unexpected node `{}`\", name_str.escape_default()),\n                        ));\n                    }\n                }\n            }\n        }\n\n        let kind = if let Some(spring_params) = spring_params {\n            // Configured spring.\n            Kind::Spring(spring_params)\n        } else if easing_params == OptionalEasingParams::default() {\n            // Did not configure anything.\n            default.kind\n        } else {\n            // Configured easing.\n            let default = if let Kind::Easing(easing) = default.kind {\n                easing\n            } else {\n                // Generic fallback values for when the default animation is spring, but the user\n                // configured an easing animation.\n                EasingParams {\n                    duration_ms: 250,\n                    curve: Curve::EaseOutCubic,\n                }\n            };\n\n            Kind::Easing(EasingParams {\n                duration_ms: easing_params.duration_ms.unwrap_or(default.duration_ms),\n                curve: easing_params.curve.unwrap_or(default.curve),\n            })\n        };\n\n        Ok(Self { off, kind })\n    }\n}\n\nimpl<S> knuffel::Decode<S> for SpringParams\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        if let Some(type_name) = &node.type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n        if let Some(val) = node.arguments.first() {\n            ctx.emit_error(DecodeError::unexpected(\n                &val.literal,\n                \"argument\",\n                \"unexpected argument\",\n            ));\n        }\n        for child in node.children() {\n            ctx.emit_error(DecodeError::unexpected(\n                child,\n                \"node\",\n                format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n            ));\n        }\n\n        let mut damping_ratio = None;\n        let mut stiffness = None;\n        let mut epsilon = None;\n        for (name, val) in &node.properties {\n            match &***name {\n                \"damping-ratio\" => {\n                    damping_ratio = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);\n                }\n                \"stiffness\" => {\n                    stiffness = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);\n                }\n                \"epsilon\" => {\n                    epsilon = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);\n                }\n                name_str => {\n                    ctx.emit_error(DecodeError::unexpected(\n                        name,\n                        \"property\",\n                        format!(\"unexpected property `{}`\", name_str.escape_default()),\n                    ));\n                }\n            }\n        }\n        let damping_ratio = damping_ratio\n            .ok_or_else(|| DecodeError::missing(node, \"property `damping-ratio` is required\"))?;\n        let stiffness = stiffness\n            .ok_or_else(|| DecodeError::missing(node, \"property `stiffness` is required\"))?;\n        let epsilon =\n            epsilon.ok_or_else(|| DecodeError::missing(node, \"property `epsilon` is required\"))?;\n\n        if !(0.1..=10.).contains(&damping_ratio) {\n            ctx.emit_error(DecodeError::conversion(\n                node,\n                \"damping-ratio must be between 0.1 and 10.0\",\n            ));\n        }\n        if stiffness < 1 {\n            ctx.emit_error(DecodeError::conversion(node, \"stiffness must be >= 1\"));\n        }\n        if !(0.00001..=0.1).contains(&epsilon) {\n            ctx.emit_error(DecodeError::conversion(\n                node,\n                \"epsilon must be between 0.00001 and 0.1\",\n            ));\n        }\n\n        Ok(SpringParams {\n            damping_ratio,\n            stiffness,\n            epsilon,\n        })\n    }\n}\n"
  },
  {
    "path": "niri-config/src/appearance.rs",
    "content": "use std::ops::{Mul, MulAssign};\nuse std::str::FromStr;\n\nuse knuffel::errors::DecodeError;\nuse miette::{miette, IntoDiagnostic as _};\nuse smithay::backend::renderer::Color32F;\n\nuse crate::utils::{Flag, MergeWith};\nuse crate::FloatOrInt;\n\npub const DEFAULT_BACKGROUND_COLOR: Color = Color::from_array_unpremul([0.25, 0.25, 0.25, 1.]);\npub const DEFAULT_BACKDROP_COLOR: Color = Color::from_array_unpremul([0.15, 0.15, 0.15, 1.]);\n\n/// RGB color in [0, 1] with unpremultiplied alpha.\n#[derive(Debug, Default, Clone, Copy, PartialEq)]\npub struct Color {\n    pub r: f32,\n    pub g: f32,\n    pub b: f32,\n    pub a: f32,\n}\n\nimpl Color {\n    pub const fn new_unpremul(r: f32, g: f32, b: f32, a: f32) -> Self {\n        Self { r, g, b, a }\n    }\n\n    pub fn from_rgba8_unpremul(r: u8, g: u8, b: u8, a: u8) -> Self {\n        Self::from_array_unpremul([r, g, b, a].map(|x| x as f32 / 255.))\n    }\n\n    pub fn from_array_premul([r, g, b, a]: [f32; 4]) -> Self {\n        let a = a.clamp(0., 1.);\n\n        if a == 0. {\n            Self::new_unpremul(0., 0., 0., 0.)\n        } else {\n            Self {\n                r: (r / a).clamp(0., 1.),\n                g: (g / a).clamp(0., 1.),\n                b: (b / a).clamp(0., 1.),\n                a,\n            }\n        }\n    }\n\n    pub const fn from_array_unpremul([r, g, b, a]: [f32; 4]) -> Self {\n        Self { r, g, b, a }\n    }\n\n    pub fn from_color32f(color: Color32F) -> Self {\n        Self::from_array_premul(color.components())\n    }\n\n    pub fn to_array_unpremul(self) -> [f32; 4] {\n        [self.r, self.g, self.b, self.a]\n    }\n\n    pub fn to_array_premul(self) -> [f32; 4] {\n        let [r, g, b, a] = [self.r, self.g, self.b, self.a];\n        [r * a, g * a, b * a, a]\n    }\n}\n\nimpl Mul<f32> for Color {\n    type Output = Self;\n\n    fn mul(mut self, rhs: f32) -> Self::Output {\n        self.a *= rhs;\n        self\n    }\n}\n\nimpl MulAssign<f32> for Color {\n    fn mul_assign(&mut self, rhs: f32) {\n        self.a *= rhs;\n    }\n}\n\nimpl From<Color> for Color32F {\n    fn from(value: Color) -> Self {\n        Color32F::from(value.to_array_premul())\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct Gradient {\n    #[knuffel(property, str)]\n    pub from: Color,\n    #[knuffel(property, str)]\n    pub to: Color,\n    #[knuffel(property, default = 180)]\n    pub angle: i16,\n    #[knuffel(property, default)]\n    pub relative_to: GradientRelativeTo,\n    #[knuffel(property(name = \"in\"), str, default)]\n    pub in_: GradientInterpolation,\n}\n\nimpl From<Color> for Gradient {\n    fn from(value: Color) -> Self {\n        Self {\n            from: value,\n            to: value,\n            angle: 0,\n            relative_to: GradientRelativeTo::Window,\n            in_: GradientInterpolation::default(),\n        }\n    }\n}\n\n#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum GradientRelativeTo {\n    #[default]\n    Window,\n    WorkspaceView,\n}\n\n#[derive(Default, Debug, Clone, Copy, PartialEq)]\npub struct GradientInterpolation {\n    pub color_space: GradientColorSpace,\n    pub hue_interpolation: HueInterpolation,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum GradientColorSpace {\n    #[default]\n    Srgb,\n    SrgbLinear,\n    Oklab,\n    Oklch,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum HueInterpolation {\n    #[default]\n    Shorter,\n    Longer,\n    Increasing,\n    Decreasing,\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq)]\npub struct CornerRadius {\n    pub top_left: f32,\n    pub top_right: f32,\n    pub bottom_right: f32,\n    pub bottom_left: f32,\n}\n\nimpl From<CornerRadius> for [f32; 4] {\n    fn from(value: CornerRadius) -> Self {\n        [\n            value.top_left,\n            value.top_right,\n            value.bottom_right,\n            value.bottom_left,\n        ]\n    }\n}\n\nimpl From<f32> for CornerRadius {\n    fn from(value: f32) -> Self {\n        Self {\n            top_left: value,\n            top_right: value,\n            bottom_right: value,\n            bottom_left: value,\n        }\n    }\n}\n\nimpl CornerRadius {\n    pub fn fit_to(self, width: f32, height: f32) -> Self {\n        // Like in CSS: https://drafts.csswg.org/css-backgrounds/#corner-overlap\n        let reduction = f32::min(\n            f32::min(\n                width / (self.top_left + self.top_right),\n                width / (self.bottom_left + self.bottom_right),\n            ),\n            f32::min(\n                height / (self.top_left + self.bottom_left),\n                height / (self.top_right + self.bottom_right),\n            ),\n        );\n        let reduction = f32::min(1., reduction);\n\n        Self {\n            top_left: self.top_left * reduction,\n            top_right: self.top_right * reduction,\n            bottom_right: self.bottom_right * reduction,\n            bottom_left: self.bottom_left * reduction,\n        }\n    }\n\n    pub fn expanded_by(mut self, width: f32) -> Self {\n        // Radius = 0 is preserved, so that square corners remain square.\n        if self.top_left > 0. {\n            self.top_left += width;\n        }\n        if self.top_right > 0. {\n            self.top_right += width;\n        }\n        if self.bottom_right > 0. {\n            self.bottom_right += width;\n        }\n        if self.bottom_left > 0. {\n            self.bottom_left += width;\n        }\n\n        if width < 0. {\n            self.top_left = self.top_left.max(0.);\n            self.top_right = self.top_right.max(0.);\n            self.bottom_left = self.bottom_left.max(0.);\n            self.bottom_right = self.bottom_right.max(0.);\n        }\n\n        self\n    }\n\n    pub fn scaled_by(self, scale: f32) -> Self {\n        Self {\n            top_left: self.top_left * scale,\n            top_right: self.top_right * scale,\n            bottom_right: self.bottom_right * scale,\n            bottom_left: self.bottom_left * scale,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct FocusRing {\n    pub off: bool,\n    pub width: f64,\n    pub active_color: Color,\n    pub inactive_color: Color,\n    pub urgent_color: Color,\n    pub active_gradient: Option<Gradient>,\n    pub inactive_gradient: Option<Gradient>,\n    pub urgent_gradient: Option<Gradient>,\n}\n\nimpl Default for FocusRing {\n    fn default() -> Self {\n        Self {\n            off: false,\n            width: 4.,\n            active_color: Color::from_rgba8_unpremul(127, 200, 255, 255),\n            inactive_color: Color::from_rgba8_unpremul(80, 80, 80, 255),\n            urgent_color: Color::from_rgba8_unpremul(155, 0, 0, 255),\n            active_gradient: None,\n            inactive_gradient: None,\n            urgent_gradient: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Border {\n    pub off: bool,\n    pub width: f64,\n    pub active_color: Color,\n    pub inactive_color: Color,\n    pub urgent_color: Color,\n    pub active_gradient: Option<Gradient>,\n    pub inactive_gradient: Option<Gradient>,\n    pub urgent_gradient: Option<Gradient>,\n}\n\nimpl Default for Border {\n    fn default() -> Self {\n        Self {\n            off: true,\n            width: 4.,\n            active_color: Color::from_rgba8_unpremul(255, 200, 127, 255),\n            inactive_color: Color::from_rgba8_unpremul(80, 80, 80, 255),\n            urgent_color: Color::from_rgba8_unpremul(155, 0, 0, 255),\n            active_gradient: None,\n            inactive_gradient: None,\n            urgent_gradient: None,\n        }\n    }\n}\n\nimpl From<Border> for FocusRing {\n    fn from(value: Border) -> Self {\n        Self {\n            off: value.off,\n            width: value.width,\n            active_color: value.active_color,\n            inactive_color: value.inactive_color,\n            urgent_color: value.urgent_color,\n            active_gradient: value.active_gradient,\n            inactive_gradient: value.inactive_gradient,\n            urgent_gradient: value.urgent_gradient,\n        }\n    }\n}\n\nimpl From<FocusRing> for Border {\n    fn from(value: FocusRing) -> Self {\n        Self {\n            off: value.off,\n            width: value.width,\n            active_color: value.active_color,\n            inactive_color: value.inactive_color,\n            urgent_color: value.urgent_color,\n            active_gradient: value.active_gradient,\n            inactive_gradient: value.inactive_gradient,\n            urgent_gradient: value.urgent_gradient,\n        }\n    }\n}\n\nimpl MergeWith<BorderRule> for Border {\n    fn merge_with(&mut self, part: &BorderRule) {\n        self.off |= part.off;\n        if part.on {\n            self.off = false;\n        }\n\n        merge!((self, part), width);\n\n        merge_color_gradient!(\n            (self, part),\n            (active_color, active_gradient),\n            (inactive_color, inactive_gradient),\n            (urgent_color, urgent_gradient),\n        );\n    }\n}\n\nimpl MergeWith<BorderRule> for FocusRing {\n    fn merge_with(&mut self, part: &BorderRule) {\n        let mut x = Border::from(*self);\n        x.merge_with(part);\n        *self = FocusRing::from(x);\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Shadow {\n    pub on: bool,\n    pub offset: ShadowOffset,\n    pub softness: f64,\n    pub spread: f64,\n    pub draw_behind_window: bool,\n    pub color: Color,\n    pub inactive_color: Option<Color>,\n}\n\nimpl Default for Shadow {\n    fn default() -> Self {\n        Self {\n            on: false,\n            offset: ShadowOffset {\n                x: FloatOrInt(0.),\n                y: FloatOrInt(5.),\n            },\n            softness: 30.,\n            spread: 5.,\n            draw_behind_window: false,\n            color: Color::from_rgba8_unpremul(0, 0, 0, 0x77),\n            inactive_color: None,\n        }\n    }\n}\n\nimpl MergeWith<ShadowRule> for Shadow {\n    fn merge_with(&mut self, part: &ShadowRule) {\n        self.on |= part.on;\n        if part.off {\n            self.on = false;\n        }\n\n        merge!((self, part), softness, spread);\n\n        merge_clone!((self, part), offset, draw_behind_window, color);\n\n        merge_clone_opt!((self, part), inactive_color);\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct ShadowOffset {\n    #[knuffel(property, default)]\n    pub x: FloatOrInt<-65535, 65535>,\n    #[knuffel(property, default)]\n    pub y: FloatOrInt<-65535, 65535>,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct WorkspaceShadow {\n    pub off: bool,\n    pub offset: ShadowOffset,\n    pub softness: f64,\n    pub spread: f64,\n    pub color: Color,\n}\n\nimpl Default for WorkspaceShadow {\n    fn default() -> Self {\n        Self {\n            off: false,\n            offset: ShadowOffset {\n                x: FloatOrInt(0.),\n                y: FloatOrInt(10.),\n            },\n            softness: 40.,\n            spread: 10.,\n            color: Color::from_rgba8_unpremul(0, 0, 0, 0x50),\n        }\n    }\n}\n\nimpl From<WorkspaceShadow> for Shadow {\n    fn from(value: WorkspaceShadow) -> Self {\n        Self {\n            on: !value.off,\n            offset: value.offset,\n            softness: value.softness,\n            spread: value.spread,\n            draw_behind_window: false,\n            color: value.color,\n            inactive_color: None,\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct WorkspaceShadowPart {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child)]\n    pub offset: Option<ShadowOffset>,\n    #[knuffel(child, unwrap(argument))]\n    pub softness: Option<FloatOrInt<0, 1024>>,\n    #[knuffel(child, unwrap(argument))]\n    pub spread: Option<FloatOrInt<-1024, 1024>>,\n    #[knuffel(child)]\n    pub color: Option<Color>,\n}\n\nimpl MergeWith<WorkspaceShadowPart> for WorkspaceShadow {\n    fn merge_with(&mut self, part: &WorkspaceShadowPart) {\n        self.off |= part.off;\n        if part.on {\n            self.off = false;\n        }\n\n        merge_clone!((self, part), offset, color);\n        merge!((self, part), softness, spread);\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct TabIndicator {\n    pub off: bool,\n    pub hide_when_single_tab: bool,\n    pub place_within_column: bool,\n    pub gap: f64,\n    pub width: f64,\n    pub length: TabIndicatorLength,\n    pub position: TabIndicatorPosition,\n    pub gaps_between_tabs: f64,\n    pub corner_radius: f64,\n    pub active_color: Option<Color>,\n    pub inactive_color: Option<Color>,\n    pub urgent_color: Option<Color>,\n    pub active_gradient: Option<Gradient>,\n    pub inactive_gradient: Option<Gradient>,\n    pub urgent_gradient: Option<Gradient>,\n}\n\nimpl Default for TabIndicator {\n    fn default() -> Self {\n        Self {\n            off: false,\n            hide_when_single_tab: false,\n            place_within_column: false,\n            gap: 5.,\n            width: 4.,\n            length: TabIndicatorLength {\n                total_proportion: Some(0.5),\n            },\n            position: TabIndicatorPosition::Left,\n            gaps_between_tabs: 0.,\n            corner_radius: 0.,\n            active_color: None,\n            inactive_color: None,\n            urgent_color: None,\n            active_gradient: None,\n            inactive_gradient: None,\n            urgent_gradient: None,\n        }\n    }\n}\n\nimpl MergeWith<TabIndicatorPart> for TabIndicator {\n    fn merge_with(&mut self, part: &TabIndicatorPart) {\n        self.off |= part.off;\n        if part.on {\n            self.off = false;\n        }\n\n        merge!(\n            (self, part),\n            hide_when_single_tab,\n            place_within_column,\n            gap,\n            width,\n            gaps_between_tabs,\n            corner_radius,\n        );\n\n        merge_clone!((self, part), length, position);\n\n        merge_color_gradient_opt!(\n            (self, part),\n            (active_color, active_gradient),\n            (inactive_color, inactive_gradient),\n            (urgent_color, urgent_gradient),\n        );\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct TabIndicatorPart {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child)]\n    pub hide_when_single_tab: Option<Flag>,\n    #[knuffel(child)]\n    pub place_within_column: Option<Flag>,\n    #[knuffel(child, unwrap(argument))]\n    pub gap: Option<FloatOrInt<-65535, 65535>>,\n    #[knuffel(child, unwrap(argument))]\n    pub width: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child)]\n    pub length: Option<TabIndicatorLength>,\n    #[knuffel(child, unwrap(argument))]\n    pub position: Option<TabIndicatorPosition>,\n    #[knuffel(child, unwrap(argument))]\n    pub gaps_between_tabs: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child, unwrap(argument))]\n    pub corner_radius: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child)]\n    pub active_color: Option<Color>,\n    #[knuffel(child)]\n    pub inactive_color: Option<Color>,\n    #[knuffel(child)]\n    pub urgent_color: Option<Color>,\n    #[knuffel(child)]\n    pub active_gradient: Option<Gradient>,\n    #[knuffel(child)]\n    pub inactive_gradient: Option<Gradient>,\n    #[knuffel(child)]\n    pub urgent_gradient: Option<Gradient>,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct TabIndicatorLength {\n    #[knuffel(property)]\n    pub total_proportion: Option<f64>,\n}\n\n#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq)]\npub enum TabIndicatorPosition {\n    Left,\n    Right,\n    Top,\n    Bottom,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct InsertHint {\n    pub off: bool,\n    pub color: Color,\n    pub gradient: Option<Gradient>,\n}\n\nimpl Default for InsertHint {\n    fn default() -> Self {\n        Self {\n            off: false,\n            color: Color::from_rgba8_unpremul(127, 200, 255, 128),\n            gradient: None,\n        }\n    }\n}\n\nimpl MergeWith<InsertHintPart> for InsertHint {\n    fn merge_with(&mut self, part: &InsertHintPart) {\n        self.off |= part.off;\n        if part.on {\n            self.off = false;\n        }\n\n        merge_color_gradient!((self, part), (color, gradient));\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct InsertHintPart {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child)]\n    pub color: Option<Color>,\n    #[knuffel(child)]\n    pub gradient: Option<Gradient>,\n}\n\n#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq, Eq)]\npub enum BlockOutFrom {\n    Screencast,\n    ScreenCapture,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct BorderRule {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child, unwrap(argument))]\n    pub width: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child)]\n    pub active_color: Option<Color>,\n    #[knuffel(child)]\n    pub inactive_color: Option<Color>,\n    #[knuffel(child)]\n    pub urgent_color: Option<Color>,\n    #[knuffel(child)]\n    pub active_gradient: Option<Gradient>,\n    #[knuffel(child)]\n    pub inactive_gradient: Option<Gradient>,\n    #[knuffel(child)]\n    pub urgent_gradient: Option<Gradient>,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct ShadowRule {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child)]\n    pub offset: Option<ShadowOffset>,\n    #[knuffel(child, unwrap(argument))]\n    pub softness: Option<FloatOrInt<0, 1024>>,\n    #[knuffel(child, unwrap(argument))]\n    pub spread: Option<FloatOrInt<-1024, 1024>>,\n    #[knuffel(child, unwrap(argument))]\n    pub draw_behind_window: Option<bool>,\n    #[knuffel(child)]\n    pub color: Option<Color>,\n    #[knuffel(child)]\n    pub inactive_color: Option<Color>,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct TabIndicatorRule {\n    #[knuffel(child)]\n    pub active_color: Option<Color>,\n    #[knuffel(child)]\n    pub inactive_color: Option<Color>,\n    #[knuffel(child)]\n    pub urgent_color: Option<Color>,\n    #[knuffel(child)]\n    pub active_gradient: Option<Gradient>,\n    #[knuffel(child)]\n    pub inactive_gradient: Option<Gradient>,\n    #[knuffel(child)]\n    pub urgent_gradient: Option<Gradient>,\n}\n\nimpl MergeWith<Self> for BorderRule {\n    fn merge_with(&mut self, part: &Self) {\n        merge_on_off!((self, part));\n\n        merge_clone_opt!((self, part), width);\n\n        merge_color_gradient_opt!(\n            (self, part),\n            (active_color, active_gradient),\n            (inactive_color, inactive_gradient),\n            (urgent_color, urgent_gradient),\n        );\n    }\n}\n\nimpl MergeWith<Self> for ShadowRule {\n    fn merge_with(&mut self, part: &Self) {\n        merge_on_off!((self, part));\n\n        merge_clone_opt!(\n            (self, part),\n            offset,\n            softness,\n            spread,\n            draw_behind_window,\n            color,\n            inactive_color,\n        );\n    }\n}\n\nimpl MergeWith<Self> for TabIndicatorRule {\n    fn merge_with(&mut self, part: &Self) {\n        merge_color_gradient_opt!(\n            (self, part),\n            (active_color, active_gradient),\n            (inactive_color, inactive_gradient),\n            (urgent_color, urgent_gradient),\n        );\n    }\n}\n\nimpl FromStr for GradientInterpolation {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut iter = s.split_whitespace();\n        let in_part1 = iter.next();\n        let in_part2 = iter.next();\n        let in_part3 = iter.next();\n\n        let Some(in_part1) = in_part1 else {\n            return Err(miette!(\"missing color space\"));\n        };\n\n        let color = match in_part1 {\n            \"srgb\" => GradientColorSpace::Srgb,\n            \"srgb-linear\" => GradientColorSpace::SrgbLinear,\n            \"oklab\" => GradientColorSpace::Oklab,\n            \"oklch\" => GradientColorSpace::Oklch,\n            x => {\n                return Err(miette!(\n                    \"invalid color space {x}; can be srgb, srgb-linear, oklab or oklch\"\n                ))\n            }\n        };\n\n        let interpolation = if let Some(in_part2) = in_part2 {\n            if color != GradientColorSpace::Oklch {\n                return Err(miette!(\"only oklch color space can have hue interpolation\"));\n            }\n\n            if in_part3 != Some(\"hue\") {\n                return Err(miette!(\n                    \"interpolation must end with \\\"hue\\\", like \\\"oklch shorter hue\\\"\"\n                ));\n            } else if iter.next().is_some() {\n                return Err(miette!(\"unexpected text after hue interpolation\"));\n            } else {\n                match in_part2 {\n                    \"shorter\" => HueInterpolation::Shorter,\n                    \"longer\" => HueInterpolation::Longer,\n                    \"increasing\" => HueInterpolation::Increasing,\n                    \"decreasing\" => HueInterpolation::Decreasing,\n                    x => {\n                        return Err(miette!(\n                            \"invalid hue interpolation {x}; \\\n                             can be shorter, longer, increasing, decreasing\"\n                        ))\n                    }\n                }\n            }\n        } else {\n            HueInterpolation::default()\n        };\n\n        Ok(Self {\n            color_space: color,\n            hue_interpolation: interpolation,\n        })\n    }\n}\n\nimpl FromStr for Color {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let color = csscolorparser::parse(s)\n            .into_diagnostic()?\n            .clamp()\n            .to_array();\n        Ok(Self::from_array_unpremul(color))\n    }\n}\n\n#[derive(knuffel::Decode)]\nstruct ColorRgba {\n    #[knuffel(argument)]\n    r: u8,\n    #[knuffel(argument)]\n    g: u8,\n    #[knuffel(argument)]\n    b: u8,\n    #[knuffel(argument)]\n    a: u8,\n}\n\nimpl From<ColorRgba> for Color {\n    fn from(value: ColorRgba) -> Self {\n        let ColorRgba { r, g, b, a } = value;\n        Self::from_array_unpremul([r, g, b, a].map(|x| x as f32 / 255.))\n    }\n}\n\n// Manual impl to allow both one-argument string and 4-argument RGBA forms.\nimpl<S> knuffel::Decode<S> for Color\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        // Check for unexpected type name.\n        if let Some(type_name) = &node.type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n\n        // Get the first argument.\n        let mut iter_args = node.arguments.iter();\n        let val = iter_args\n            .next()\n            .ok_or_else(|| DecodeError::missing(node, \"additional argument is required\"))?;\n\n        // Check for unexpected type name.\n        if let Some(typ) = &val.type_name {\n            ctx.emit_error(DecodeError::TypeName {\n                span: typ.span().clone(),\n                found: Some((**typ).clone()),\n                expected: knuffel::errors::ExpectedType::no_type(),\n                rust_type: \"str\",\n            });\n        }\n\n        // Check the argument type.\n        let rv = match *val.literal {\n            // If it's a string, use FromStr.\n            knuffel::ast::Literal::String(ref s) => {\n                Color::from_str(s).map_err(|e| DecodeError::conversion(&val.literal, e))\n            }\n            // Otherwise, fall back to the 4-argument RGBA form.\n            _ => return ColorRgba::decode_node(node, ctx).map(Color::from),\n        }?;\n\n        // Check for unexpected following arguments.\n        if let Some(val) = iter_args.next() {\n            ctx.emit_error(DecodeError::unexpected(\n                &val.literal,\n                \"argument\",\n                \"unexpected argument\",\n            ));\n        }\n\n        // Check for unexpected properties and children.\n        for name in node.properties.keys() {\n            ctx.emit_error(DecodeError::unexpected(\n                name,\n                \"property\",\n                format!(\"unexpected property `{}`\", name.escape_default()),\n            ));\n        }\n        for child in node.children.as_ref().map(|lst| &lst[..]).unwrap_or(&[]) {\n            ctx.emit_error(DecodeError::unexpected(\n                child,\n                \"node\",\n                format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n            ));\n        }\n\n        Ok(rv)\n    }\n}\n\nimpl<S> knuffel::Decode<S> for CornerRadius\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        // Check for unexpected type name.\n        if let Some(type_name) = &node.type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n\n        let decode_radius = |ctx: &mut knuffel::decode::Context<S>,\n                             val: &knuffel::ast::Value<S>| {\n            // Check for unexpected type name.\n            if let Some(typ) = &val.type_name {\n                ctx.emit_error(DecodeError::TypeName {\n                    span: typ.span().clone(),\n                    found: Some((**typ).clone()),\n                    expected: knuffel::errors::ExpectedType::no_type(),\n                    rust_type: \"str\",\n                });\n            }\n\n            // Decode both integers and floats.\n            let radius = match *val.literal {\n                knuffel::ast::Literal::Int(ref x) => f32::from(match x.try_into() {\n                    Ok(x) => x,\n                    Err(err) => {\n                        ctx.emit_error(DecodeError::conversion(&val.literal, err));\n                        0i16\n                    }\n                }),\n                knuffel::ast::Literal::Decimal(ref x) => match x.try_into() {\n                    Ok(x) => x,\n                    Err(err) => {\n                        ctx.emit_error(DecodeError::conversion(&val.literal, err));\n                        0.\n                    }\n                },\n                _ => {\n                    ctx.emit_error(DecodeError::scalar_kind(\n                        knuffel::decode::Kind::Int,\n                        &val.literal,\n                    ));\n                    0.\n                }\n            };\n\n            if radius < 0. {\n                ctx.emit_error(DecodeError::conversion(&val.literal, \"radius must be >= 0\"));\n            }\n\n            radius\n        };\n\n        // Get the first argument.\n        let mut iter_args = node.arguments.iter();\n        let val = iter_args\n            .next()\n            .ok_or_else(|| DecodeError::missing(node, \"additional argument is required\"))?;\n\n        let top_left = decode_radius(ctx, val);\n\n        let mut rv = CornerRadius {\n            top_left,\n            top_right: top_left,\n            bottom_right: top_left,\n            bottom_left: top_left,\n        };\n\n        if let Some(val) = iter_args.next() {\n            rv.top_right = decode_radius(ctx, val);\n\n            let val = iter_args.next().ok_or_else(|| {\n                DecodeError::missing(node, \"either 1 or 4 arguments are required\")\n            })?;\n            rv.bottom_right = decode_radius(ctx, val);\n\n            let val = iter_args.next().ok_or_else(|| {\n                DecodeError::missing(node, \"either 1 or 4 arguments are required\")\n            })?;\n            rv.bottom_left = decode_radius(ctx, val);\n\n            // Check for unexpected following arguments.\n            if let Some(val) = iter_args.next() {\n                ctx.emit_error(DecodeError::unexpected(\n                    &val.literal,\n                    \"argument\",\n                    \"unexpected argument\",\n                ));\n            }\n        }\n\n        // Check for unexpected properties and children.\n        for name in node.properties.keys() {\n            ctx.emit_error(DecodeError::unexpected(\n                name,\n                \"property\",\n                format!(\"unexpected property `{}`\", name.escape_default()),\n            ));\n        }\n        for child in node.children.as_ref().map(|lst| &lst[..]).unwrap_or(&[]) {\n            ctx.emit_error(DecodeError::unexpected(\n                child,\n                \"node\",\n                format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n            ));\n        }\n\n        Ok(rv)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::{assert_debug_snapshot, assert_snapshot};\n\n    use super::*;\n    use crate::Config;\n\n    #[test]\n    fn parse_gradient_interpolation() {\n        assert_eq!(\n            \"srgb\".parse::<GradientInterpolation>().unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::Srgb,\n                ..Default::default()\n            }\n        );\n        assert_eq!(\n            \"srgb-linear\".parse::<GradientInterpolation>().unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::SrgbLinear,\n                ..Default::default()\n            }\n        );\n        assert_eq!(\n            \"oklab\".parse::<GradientInterpolation>().unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::Oklab,\n                ..Default::default()\n            }\n        );\n        assert_eq!(\n            \"oklch\".parse::<GradientInterpolation>().unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                ..Default::default()\n            }\n        );\n        assert_eq!(\n            \"oklch shorter hue\"\n                .parse::<GradientInterpolation>()\n                .unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Shorter,\n            }\n        );\n        assert_eq!(\n            \"oklch longer hue\".parse::<GradientInterpolation>().unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Longer,\n            }\n        );\n        assert_eq!(\n            \"oklch decreasing hue\"\n                .parse::<GradientInterpolation>()\n                .unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Decreasing,\n            }\n        );\n        assert_eq!(\n            \"oklch increasing hue\"\n                .parse::<GradientInterpolation>()\n                .unwrap(),\n            GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Increasing,\n            }\n        );\n\n        assert!(\"\".parse::<GradientInterpolation>().is_err());\n        assert!(\"srgb shorter hue\".parse::<GradientInterpolation>().is_err());\n        assert!(\"oklch shorter\".parse::<GradientInterpolation>().is_err());\n        assert!(\"oklch shorter h\".parse::<GradientInterpolation>().is_err());\n        assert!(\"oklch a hue\".parse::<GradientInterpolation>().is_err());\n        assert!(\"oklch shorter hue a\"\n            .parse::<GradientInterpolation>()\n            .is_err());\n    }\n\n    #[test]\n    fn test_border_rule_on_off_merging() {\n        fn is_on(config: &str, rules: &[&str]) -> String {\n            let mut resolved = Border {\n                off: config == \"off\",\n                ..Default::default()\n            };\n\n            for rule in rules.iter().copied() {\n                let rule = BorderRule {\n                    off: rule == \"off\" || rule == \"off,on\",\n                    on: rule == \"on\" || rule == \"off,on\",\n                    ..Default::default()\n                };\n\n                resolved.merge_with(&rule);\n            }\n\n            if resolved.off { \"off\" } else { \"on\" }.to_owned()\n        }\n\n        assert_snapshot!(is_on(\"off\", &[]), @\"off\");\n        assert_snapshot!(is_on(\"off\", &[\"off\"]), @\"off\");\n        assert_snapshot!(is_on(\"off\", &[\"on\"]), @\"on\");\n        assert_snapshot!(is_on(\"off\", &[\"off,on\"]), @\"on\");\n\n        assert_snapshot!(is_on(\"on\", &[]), @\"on\");\n        assert_snapshot!(is_on(\"on\", &[\"off\"]), @\"off\");\n        assert_snapshot!(is_on(\"on\", &[\"on\"]), @\"on\");\n        assert_snapshot!(is_on(\"on\", &[\"off,on\"]), @\"on\");\n\n        assert_snapshot!(is_on(\"off\", &[\"off\", \"off\"]), @\"off\");\n        assert_snapshot!(is_on(\"off\", &[\"off\", \"on\"]), @\"on\");\n        assert_snapshot!(is_on(\"off\", &[\"on\", \"off\"]), @\"off\");\n        assert_snapshot!(is_on(\"off\", &[\"on\", \"on\"]), @\"on\");\n\n        assert_snapshot!(is_on(\"on\", &[\"off\", \"off\"]), @\"off\");\n        assert_snapshot!(is_on(\"on\", &[\"off\", \"on\"]), @\"on\");\n        assert_snapshot!(is_on(\"on\", &[\"on\", \"off\"]), @\"off\");\n        assert_snapshot!(is_on(\"on\", &[\"on\", \"on\"]), @\"on\");\n    }\n\n    #[test]\n    fn rule_color_can_override_base_gradient() {\n        let config = Config::parse_mem(\n            r##\"\n            // Start with gradient set.\n            layout {\n                border {\n                    active-gradient from=\"#101010\" to=\"#202020\"\n                    inactive-gradient from=\"#111111\" to=\"#212121\"\n                    urgent-gradient from=\"#121212\" to=\"#222222\"\n                }\n            }\n\n            // Override with color.\n            window-rule {\n                border {\n                    active-color \"#abcdef\"\n                    inactive-color \"#123456\"\n                    urgent-color \"#fedcba\"\n                }\n            }\n            \"##,\n        )\n        .unwrap();\n\n        let mut border = config.layout.border;\n        for rule in &config.window_rules {\n            border.merge_with(&rule.border);\n        }\n\n        // Gradient should be None because it's overwritten.\n        assert_debug_snapshot!(\n            (\n                border.active_gradient.is_some(),\n                border.inactive_gradient.is_some(),\n                border.urgent_gradient.is_some(),\n            ),\n            @r\"\n        (\n            false,\n            false,\n            false,\n        )\n        \"\n        );\n    }\n\n    #[test]\n    fn rule_color_can_override_rule_gradient() {\n        let config = Config::parse_mem(\n            r##\"\n            // Start with gradient set.\n            layout {\n                border {\n                    active-gradient from=\"#101010\" to=\"#202020\"\n                    inactive-gradient from=\"#111111\" to=\"#212121\"\n                    urgent-gradient from=\"#121212\" to=\"#222222\"\n                }\n            }\n\n            // Window rule with gradients set.\n            window-rule {\n                border {\n                    active-gradient from=\"#303030\" to=\"#404040\"\n                    inactive-gradient from=\"#313131\" to=\"#414141\"\n                    urgent-gradient from=\"#323232\" to=\"#424242\"\n                }\n\n                tab-indicator {\n                    active-gradient from=\"#505050\" to=\"#606060\"\n                    inactive-gradient from=\"#515151\" to=\"#616161\"\n                    urgent-gradient from=\"#525252\" to=\"#626262\"\n                }\n            }\n\n            // Override with color.\n            window-rule {\n                border {\n                    active-color \"#abcdef\"\n                    inactive-color \"#123456\"\n                    urgent-color \"#fedcba\"\n                }\n\n                tab-indicator {\n                    active-color \"#abcdef\"\n                    inactive-color \"#123456\"\n                    urgent-color \"#fedcba\"\n                }\n            }\n            \"##,\n        )\n        .unwrap();\n\n        let mut border = config.layout.border;\n        let mut tab_indicator_rule = TabIndicatorRule::default();\n        for rule in &config.window_rules {\n            border.merge_with(&rule.border);\n            tab_indicator_rule.merge_with(&rule.tab_indicator);\n        }\n\n        // Gradient should be None because it's overwritten.\n        assert_debug_snapshot!(\n            (\n                border.active_gradient.is_some(),\n                border.inactive_gradient.is_some(),\n                border.urgent_gradient.is_some(),\n                tab_indicator_rule.active_gradient.is_some(),\n                tab_indicator_rule.inactive_gradient.is_some(),\n                tab_indicator_rule.urgent_gradient.is_some(),\n            ),\n            @r\"\n        (\n            false,\n            false,\n            false,\n            false,\n            false,\n            false,\n        )\n        \"\n        );\n    }\n}\n"
  },
  {
    "path": "niri-config/src/binds.rs",
    "content": "use std::collections::HashSet;\nuse std::str::FromStr;\nuse std::time::Duration;\n\nuse bitflags::bitflags;\nuse knuffel::errors::DecodeError;\nuse miette::miette;\nuse niri_ipc::{\n    ColumnDisplay, LayoutSwitchTarget, PositionChange, SizeChange, WorkspaceReferenceArg,\n};\nuse smithay::input::keyboard::keysyms::KEY_NoSymbol;\nuse smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE, KEYSYM_NO_FLAGS};\nuse smithay::input::keyboard::Keysym;\n\nuse crate::recent_windows::{MruDirection, MruFilter, MruScope};\nuse crate::utils::{expect_only_children, MergeWith};\n\n#[derive(Debug, Default, PartialEq)]\npub struct Binds(pub Vec<Bind>);\n\n#[derive(Debug, Clone, PartialEq)]\npub struct Bind {\n    pub key: Key,\n    pub action: Action,\n    pub repeat: bool,\n    pub cooldown: Option<Duration>,\n    pub allow_when_locked: bool,\n    pub allow_inhibiting: bool,\n    pub hotkey_overlay_title: Option<Option<String>>,\n}\n\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]\npub struct Key {\n    pub trigger: Trigger,\n    pub modifiers: Modifiers,\n}\n\n#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]\npub enum Trigger {\n    Keysym(Keysym),\n    MouseLeft,\n    MouseRight,\n    MouseMiddle,\n    MouseBack,\n    MouseForward,\n    WheelScrollDown,\n    WheelScrollUp,\n    WheelScrollLeft,\n    WheelScrollRight,\n    TouchpadScrollDown,\n    TouchpadScrollUp,\n    TouchpadScrollLeft,\n    TouchpadScrollRight,\n}\n\nbitflags! {\n    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n    pub struct Modifiers : u8 {\n        const CTRL = 1;\n        const SHIFT = 1 << 1;\n        const ALT = 1 << 2;\n        const SUPER = 1 << 3;\n        const ISO_LEVEL3_SHIFT = 1 << 4;\n        const ISO_LEVEL5_SHIFT = 1 << 5;\n        const COMPOSITOR = 1 << 6;\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct SwitchBinds {\n    #[knuffel(child)]\n    pub lid_open: Option<SwitchAction>,\n    #[knuffel(child)]\n    pub lid_close: Option<SwitchAction>,\n    #[knuffel(child)]\n    pub tablet_mode_on: Option<SwitchAction>,\n    #[knuffel(child)]\n    pub tablet_mode_off: Option<SwitchAction>,\n}\n\nimpl MergeWith<SwitchBinds> for SwitchBinds {\n    fn merge_with(&mut self, part: &SwitchBinds) {\n        merge_clone_opt!(\n            (self, part),\n            lid_open,\n            lid_close,\n            tablet_mode_on,\n            tablet_mode_off,\n        );\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq)]\npub struct SwitchAction {\n    #[knuffel(child, unwrap(arguments))]\n    pub spawn: Vec<String>,\n}\n\n// Remember to add new actions to the CLI enum too.\n#[derive(knuffel::Decode, Debug, Clone, PartialEq)]\npub enum Action {\n    Quit(#[knuffel(property(name = \"skip-confirmation\"), default)] bool),\n    #[knuffel(skip)]\n    ChangeVt(i32),\n    Suspend,\n    PowerOffMonitors,\n    PowerOnMonitors,\n    ToggleDebugTint,\n    DebugToggleOpaqueRegions,\n    DebugToggleDamage,\n    Spawn(#[knuffel(arguments)] Vec<String>),\n    SpawnSh(#[knuffel(argument)] String),\n    DoScreenTransition(#[knuffel(property(name = \"delay-ms\"))] Option<u16>),\n    #[knuffel(skip)]\n    ConfirmScreenshot {\n        write_to_disk: bool,\n    },\n    #[knuffel(skip)]\n    CancelScreenshot,\n    #[knuffel(skip)]\n    ScreenshotTogglePointer,\n    Screenshot(\n        #[knuffel(property(name = \"show-pointer\"), default = true)] bool,\n        // Path; not settable from knuffel\n        Option<String>,\n    ),\n    ScreenshotScreen(\n        #[knuffel(property(name = \"write-to-disk\"), default = true)] bool,\n        #[knuffel(property(name = \"show-pointer\"), default = true)] bool,\n        // Path; not settable from knuffel\n        Option<String>,\n    ),\n    ScreenshotWindow(\n        #[knuffel(property(name = \"write-to-disk\"), default = true)] bool,\n        #[knuffel(property(name = \"show-pointer\"), default = false)] bool,\n        // Path; not settable from knuffel\n        Option<String>,\n    ),\n    #[knuffel(skip)]\n    ScreenshotWindowById {\n        id: u64,\n        write_to_disk: bool,\n        show_pointer: bool,\n        path: Option<String>,\n    },\n    ToggleKeyboardShortcutsInhibit,\n    CloseWindow,\n    #[knuffel(skip)]\n    CloseWindowById(u64),\n    FullscreenWindow,\n    #[knuffel(skip)]\n    FullscreenWindowById(u64),\n    ToggleWindowedFullscreen,\n    #[knuffel(skip)]\n    ToggleWindowedFullscreenById(u64),\n    #[knuffel(skip)]\n    FocusWindow(u64),\n    FocusWindowInColumn(#[knuffel(argument)] u8),\n    FocusWindowPrevious,\n    FocusColumnLeft,\n    #[knuffel(skip)]\n    FocusColumnLeftUnderMouse,\n    FocusColumnRight,\n    #[knuffel(skip)]\n    FocusColumnRightUnderMouse,\n    FocusColumnFirst,\n    FocusColumnLast,\n    FocusColumnRightOrFirst,\n    FocusColumnLeftOrLast,\n    FocusColumn(#[knuffel(argument)] usize),\n    FocusWindowOrMonitorUp,\n    FocusWindowOrMonitorDown,\n    FocusColumnOrMonitorLeft,\n    FocusColumnOrMonitorRight,\n    FocusWindowDown,\n    FocusWindowUp,\n    FocusWindowDownOrColumnLeft,\n    FocusWindowDownOrColumnRight,\n    FocusWindowUpOrColumnLeft,\n    FocusWindowUpOrColumnRight,\n    FocusWindowOrWorkspaceDown,\n    FocusWindowOrWorkspaceUp,\n    FocusWindowTop,\n    FocusWindowBottom,\n    FocusWindowDownOrTop,\n    FocusWindowUpOrBottom,\n    MoveColumnLeft,\n    MoveColumnRight,\n    MoveColumnToFirst,\n    MoveColumnToLast,\n    MoveColumnLeftOrToMonitorLeft,\n    MoveColumnRightOrToMonitorRight,\n    MoveColumnToIndex(#[knuffel(argument)] usize),\n    MoveWindowDown,\n    MoveWindowUp,\n    MoveWindowDownOrToWorkspaceDown,\n    MoveWindowUpOrToWorkspaceUp,\n    ConsumeOrExpelWindowLeft,\n    #[knuffel(skip)]\n    ConsumeOrExpelWindowLeftById(u64),\n    ConsumeOrExpelWindowRight,\n    #[knuffel(skip)]\n    ConsumeOrExpelWindowRightById(u64),\n    ConsumeWindowIntoColumn,\n    ExpelWindowFromColumn,\n    SwapWindowLeft,\n    SwapWindowRight,\n    ToggleColumnTabbedDisplay,\n    SetColumnDisplay(#[knuffel(argument, str)] ColumnDisplay),\n    CenterColumn,\n    CenterWindow,\n    #[knuffel(skip)]\n    CenterWindowById(u64),\n    CenterVisibleColumns,\n    FocusWorkspaceDown,\n    #[knuffel(skip)]\n    FocusWorkspaceDownUnderMouse,\n    FocusWorkspaceUp,\n    #[knuffel(skip)]\n    FocusWorkspaceUpUnderMouse,\n    FocusWorkspace(#[knuffel(argument)] WorkspaceReference),\n    FocusWorkspacePrevious,\n    MoveWindowToWorkspaceDown(#[knuffel(property(name = \"focus\"), default = true)] bool),\n    MoveWindowToWorkspaceUp(#[knuffel(property(name = \"focus\"), default = true)] bool),\n    MoveWindowToWorkspace(\n        #[knuffel(argument)] WorkspaceReference,\n        #[knuffel(property(name = \"focus\"), default = true)] bool,\n    ),\n    #[knuffel(skip)]\n    MoveWindowToWorkspaceById {\n        window_id: u64,\n        reference: WorkspaceReference,\n        focus: bool,\n    },\n    MoveColumnToWorkspaceDown(#[knuffel(property(name = \"focus\"), default = true)] bool),\n    MoveColumnToWorkspaceUp(#[knuffel(property(name = \"focus\"), default = true)] bool),\n    MoveColumnToWorkspace(\n        #[knuffel(argument)] WorkspaceReference,\n        #[knuffel(property(name = \"focus\"), default = true)] bool,\n    ),\n    MoveWorkspaceDown,\n    MoveWorkspaceUp,\n    MoveWorkspaceToIndex(#[knuffel(argument)] usize),\n    #[knuffel(skip)]\n    MoveWorkspaceToIndexByRef {\n        new_idx: usize,\n        reference: WorkspaceReference,\n    },\n    #[knuffel(skip)]\n    MoveWorkspaceToMonitorByRef {\n        output_name: String,\n        reference: WorkspaceReference,\n    },\n    MoveWorkspaceToMonitor(#[knuffel(argument)] String),\n    SetWorkspaceName(#[knuffel(argument)] String),\n    #[knuffel(skip)]\n    SetWorkspaceNameByRef {\n        name: String,\n        reference: WorkspaceReference,\n    },\n    UnsetWorkspaceName,\n    #[knuffel(skip)]\n    UnsetWorkSpaceNameByRef(#[knuffel(argument)] WorkspaceReference),\n    FocusMonitorLeft,\n    FocusMonitorRight,\n    FocusMonitorDown,\n    FocusMonitorUp,\n    FocusMonitorPrevious,\n    FocusMonitorNext,\n    FocusMonitor(#[knuffel(argument)] String),\n    MoveWindowToMonitorLeft,\n    MoveWindowToMonitorRight,\n    MoveWindowToMonitorDown,\n    MoveWindowToMonitorUp,\n    MoveWindowToMonitorPrevious,\n    MoveWindowToMonitorNext,\n    MoveWindowToMonitor(#[knuffel(argument)] String),\n    #[knuffel(skip)]\n    MoveWindowToMonitorById {\n        id: u64,\n        output: String,\n    },\n    MoveColumnToMonitorLeft,\n    MoveColumnToMonitorRight,\n    MoveColumnToMonitorDown,\n    MoveColumnToMonitorUp,\n    MoveColumnToMonitorPrevious,\n    MoveColumnToMonitorNext,\n    MoveColumnToMonitor(#[knuffel(argument)] String),\n    SetWindowWidth(#[knuffel(argument, str)] SizeChange),\n    #[knuffel(skip)]\n    SetWindowWidthById {\n        id: u64,\n        change: SizeChange,\n    },\n    SetWindowHeight(#[knuffel(argument, str)] SizeChange),\n    #[knuffel(skip)]\n    SetWindowHeightById {\n        id: u64,\n        change: SizeChange,\n    },\n    ResetWindowHeight,\n    #[knuffel(skip)]\n    ResetWindowHeightById(u64),\n    SwitchPresetColumnWidth,\n    SwitchPresetColumnWidthBack,\n    SwitchPresetWindowWidth,\n    SwitchPresetWindowWidthBack,\n    #[knuffel(skip)]\n    SwitchPresetWindowWidthById(u64),\n    #[knuffel(skip)]\n    SwitchPresetWindowWidthBackById(u64),\n    SwitchPresetWindowHeight,\n    SwitchPresetWindowHeightBack,\n    #[knuffel(skip)]\n    SwitchPresetWindowHeightById(u64),\n    #[knuffel(skip)]\n    SwitchPresetWindowHeightBackById(u64),\n    MaximizeColumn,\n    MaximizeWindowToEdges,\n    #[knuffel(skip)]\n    MaximizeWindowToEdgesById(u64),\n    SetColumnWidth(#[knuffel(argument, str)] SizeChange),\n    ExpandColumnToAvailableWidth,\n    SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget),\n    ShowHotkeyOverlay,\n    MoveWorkspaceToMonitorLeft,\n    MoveWorkspaceToMonitorRight,\n    MoveWorkspaceToMonitorDown,\n    MoveWorkspaceToMonitorUp,\n    MoveWorkspaceToMonitorPrevious,\n    MoveWorkspaceToMonitorNext,\n    ToggleWindowFloating,\n    #[knuffel(skip)]\n    ToggleWindowFloatingById(u64),\n    MoveWindowToFloating,\n    #[knuffel(skip)]\n    MoveWindowToFloatingById(u64),\n    MoveWindowToTiling,\n    #[knuffel(skip)]\n    MoveWindowToTilingById(u64),\n    FocusFloating,\n    FocusTiling,\n    SwitchFocusBetweenFloatingAndTiling,\n    #[knuffel(skip)]\n    MoveFloatingWindowById {\n        id: Option<u64>,\n        x: PositionChange,\n        y: PositionChange,\n    },\n    ToggleWindowRuleOpacity,\n    #[knuffel(skip)]\n    ToggleWindowRuleOpacityById(u64),\n    SetDynamicCastWindow,\n    #[knuffel(skip)]\n    SetDynamicCastWindowById(u64),\n    SetDynamicCastMonitor(#[knuffel(argument)] Option<String>),\n    ClearDynamicCastTarget,\n    #[knuffel(skip)]\n    StopCast(u64),\n    ToggleOverview,\n    OpenOverview,\n    CloseOverview,\n    #[knuffel(skip)]\n    ToggleWindowUrgent(u64),\n    #[knuffel(skip)]\n    SetWindowUrgent(u64),\n    #[knuffel(skip)]\n    UnsetWindowUrgent(u64),\n    #[knuffel(skip)]\n    LoadConfigFile(#[knuffel(argument)] Option<String>),\n    #[knuffel(skip)]\n    MruAdvance {\n        direction: MruDirection,\n        scope: Option<MruScope>,\n        filter: Option<MruFilter>,\n    },\n    #[knuffel(skip)]\n    MruConfirm,\n    #[knuffel(skip)]\n    MruCancel,\n    #[knuffel(skip)]\n    MruCloseCurrentWindow,\n    #[knuffel(skip)]\n    MruFirst,\n    #[knuffel(skip)]\n    MruLast,\n    #[knuffel(skip)]\n    MruSetScope(MruScope),\n    #[knuffel(skip)]\n    MruCycleScope,\n}\n\nimpl From<niri_ipc::Action> for Action {\n    fn from(value: niri_ipc::Action) -> Self {\n        match value {\n            niri_ipc::Action::Quit { skip_confirmation } => Self::Quit(skip_confirmation),\n            niri_ipc::Action::PowerOffMonitors {} => Self::PowerOffMonitors,\n            niri_ipc::Action::PowerOnMonitors {} => Self::PowerOnMonitors,\n            niri_ipc::Action::Spawn { command } => Self::Spawn(command),\n            niri_ipc::Action::SpawnSh { command } => Self::SpawnSh(command),\n            niri_ipc::Action::DoScreenTransition { delay_ms } => Self::DoScreenTransition(delay_ms),\n            niri_ipc::Action::Screenshot { show_pointer, path } => {\n                Self::Screenshot(show_pointer, path)\n            }\n            niri_ipc::Action::ScreenshotScreen {\n                write_to_disk,\n                show_pointer,\n                path,\n            } => Self::ScreenshotScreen(write_to_disk, show_pointer, path),\n            niri_ipc::Action::ScreenshotWindow {\n                id: None,\n                write_to_disk,\n                show_pointer,\n                path,\n            } => Self::ScreenshotWindow(write_to_disk, show_pointer, path),\n            niri_ipc::Action::ScreenshotWindow {\n                id: Some(id),\n                write_to_disk,\n                show_pointer,\n                path,\n            } => Self::ScreenshotWindowById {\n                id,\n                write_to_disk,\n                show_pointer,\n                path,\n            },\n            niri_ipc::Action::ToggleKeyboardShortcutsInhibit {} => {\n                Self::ToggleKeyboardShortcutsInhibit\n            }\n            niri_ipc::Action::CloseWindow { id: None } => Self::CloseWindow,\n            niri_ipc::Action::CloseWindow { id: Some(id) } => Self::CloseWindowById(id),\n            niri_ipc::Action::FullscreenWindow { id: None } => Self::FullscreenWindow,\n            niri_ipc::Action::FullscreenWindow { id: Some(id) } => Self::FullscreenWindowById(id),\n            niri_ipc::Action::ToggleWindowedFullscreen { id: None } => {\n                Self::ToggleWindowedFullscreen\n            }\n            niri_ipc::Action::ToggleWindowedFullscreen { id: Some(id) } => {\n                Self::ToggleWindowedFullscreenById(id)\n            }\n            niri_ipc::Action::FocusWindow { id } => Self::FocusWindow(id),\n            niri_ipc::Action::FocusWindowInColumn { index } => Self::FocusWindowInColumn(index),\n            niri_ipc::Action::FocusWindowPrevious {} => Self::FocusWindowPrevious,\n            niri_ipc::Action::FocusColumnLeft {} => Self::FocusColumnLeft,\n            niri_ipc::Action::FocusColumnRight {} => Self::FocusColumnRight,\n            niri_ipc::Action::FocusColumnFirst {} => Self::FocusColumnFirst,\n            niri_ipc::Action::FocusColumnLast {} => Self::FocusColumnLast,\n            niri_ipc::Action::FocusColumnRightOrFirst {} => Self::FocusColumnRightOrFirst,\n            niri_ipc::Action::FocusColumnLeftOrLast {} => Self::FocusColumnLeftOrLast,\n            niri_ipc::Action::FocusColumn { index } => Self::FocusColumn(index),\n            niri_ipc::Action::FocusWindowOrMonitorUp {} => Self::FocusWindowOrMonitorUp,\n            niri_ipc::Action::FocusWindowOrMonitorDown {} => Self::FocusWindowOrMonitorDown,\n            niri_ipc::Action::FocusColumnOrMonitorLeft {} => Self::FocusColumnOrMonitorLeft,\n            niri_ipc::Action::FocusColumnOrMonitorRight {} => Self::FocusColumnOrMonitorRight,\n            niri_ipc::Action::FocusWindowDown {} => Self::FocusWindowDown,\n            niri_ipc::Action::FocusWindowUp {} => Self::FocusWindowUp,\n            niri_ipc::Action::FocusWindowDownOrColumnLeft {} => Self::FocusWindowDownOrColumnLeft,\n            niri_ipc::Action::FocusWindowDownOrColumnRight {} => Self::FocusWindowDownOrColumnRight,\n            niri_ipc::Action::FocusWindowUpOrColumnLeft {} => Self::FocusWindowUpOrColumnLeft,\n            niri_ipc::Action::FocusWindowUpOrColumnRight {} => Self::FocusWindowUpOrColumnRight,\n            niri_ipc::Action::FocusWindowOrWorkspaceDown {} => Self::FocusWindowOrWorkspaceDown,\n            niri_ipc::Action::FocusWindowOrWorkspaceUp {} => Self::FocusWindowOrWorkspaceUp,\n            niri_ipc::Action::FocusWindowTop {} => Self::FocusWindowTop,\n            niri_ipc::Action::FocusWindowBottom {} => Self::FocusWindowBottom,\n            niri_ipc::Action::FocusWindowDownOrTop {} => Self::FocusWindowDownOrTop,\n            niri_ipc::Action::FocusWindowUpOrBottom {} => Self::FocusWindowUpOrBottom,\n            niri_ipc::Action::MoveColumnLeft {} => Self::MoveColumnLeft,\n            niri_ipc::Action::MoveColumnRight {} => Self::MoveColumnRight,\n            niri_ipc::Action::MoveColumnToFirst {} => Self::MoveColumnToFirst,\n            niri_ipc::Action::MoveColumnToLast {} => Self::MoveColumnToLast,\n            niri_ipc::Action::MoveColumnToIndex { index } => Self::MoveColumnToIndex(index),\n            niri_ipc::Action::MoveColumnLeftOrToMonitorLeft {} => {\n                Self::MoveColumnLeftOrToMonitorLeft\n            }\n            niri_ipc::Action::MoveColumnRightOrToMonitorRight {} => {\n                Self::MoveColumnRightOrToMonitorRight\n            }\n            niri_ipc::Action::MoveWindowDown {} => Self::MoveWindowDown,\n            niri_ipc::Action::MoveWindowUp {} => Self::MoveWindowUp,\n            niri_ipc::Action::MoveWindowDownOrToWorkspaceDown {} => {\n                Self::MoveWindowDownOrToWorkspaceDown\n            }\n            niri_ipc::Action::MoveWindowUpOrToWorkspaceUp {} => Self::MoveWindowUpOrToWorkspaceUp,\n            niri_ipc::Action::ConsumeOrExpelWindowLeft { id: None } => {\n                Self::ConsumeOrExpelWindowLeft\n            }\n            niri_ipc::Action::ConsumeOrExpelWindowLeft { id: Some(id) } => {\n                Self::ConsumeOrExpelWindowLeftById(id)\n            }\n            niri_ipc::Action::ConsumeOrExpelWindowRight { id: None } => {\n                Self::ConsumeOrExpelWindowRight\n            }\n            niri_ipc::Action::ConsumeOrExpelWindowRight { id: Some(id) } => {\n                Self::ConsumeOrExpelWindowRightById(id)\n            }\n            niri_ipc::Action::ConsumeWindowIntoColumn {} => Self::ConsumeWindowIntoColumn,\n            niri_ipc::Action::ExpelWindowFromColumn {} => Self::ExpelWindowFromColumn,\n            niri_ipc::Action::SwapWindowRight {} => Self::SwapWindowRight,\n            niri_ipc::Action::SwapWindowLeft {} => Self::SwapWindowLeft,\n            niri_ipc::Action::ToggleColumnTabbedDisplay {} => Self::ToggleColumnTabbedDisplay,\n            niri_ipc::Action::SetColumnDisplay { display } => Self::SetColumnDisplay(display),\n            niri_ipc::Action::CenterColumn {} => Self::CenterColumn,\n            niri_ipc::Action::CenterWindow { id: None } => Self::CenterWindow,\n            niri_ipc::Action::CenterWindow { id: Some(id) } => Self::CenterWindowById(id),\n            niri_ipc::Action::CenterVisibleColumns {} => Self::CenterVisibleColumns,\n            niri_ipc::Action::FocusWorkspaceDown {} => Self::FocusWorkspaceDown,\n            niri_ipc::Action::FocusWorkspaceUp {} => Self::FocusWorkspaceUp,\n            niri_ipc::Action::FocusWorkspace { reference } => {\n                Self::FocusWorkspace(WorkspaceReference::from(reference))\n            }\n            niri_ipc::Action::FocusWorkspacePrevious {} => Self::FocusWorkspacePrevious,\n            niri_ipc::Action::MoveWindowToWorkspaceDown { focus } => {\n                Self::MoveWindowToWorkspaceDown(focus)\n            }\n            niri_ipc::Action::MoveWindowToWorkspaceUp { focus } => {\n                Self::MoveWindowToWorkspaceUp(focus)\n            }\n            niri_ipc::Action::MoveWindowToWorkspace {\n                window_id: None,\n                reference,\n                focus,\n            } => Self::MoveWindowToWorkspace(WorkspaceReference::from(reference), focus),\n            niri_ipc::Action::MoveWindowToWorkspace {\n                window_id: Some(window_id),\n                reference,\n                focus,\n            } => Self::MoveWindowToWorkspaceById {\n                window_id,\n                reference: WorkspaceReference::from(reference),\n                focus,\n            },\n            niri_ipc::Action::MoveColumnToWorkspaceDown { focus } => {\n                Self::MoveColumnToWorkspaceDown(focus)\n            }\n            niri_ipc::Action::MoveColumnToWorkspaceUp { focus } => {\n                Self::MoveColumnToWorkspaceUp(focus)\n            }\n            niri_ipc::Action::MoveColumnToWorkspace { reference, focus } => {\n                Self::MoveColumnToWorkspace(WorkspaceReference::from(reference), focus)\n            }\n            niri_ipc::Action::MoveWorkspaceDown {} => Self::MoveWorkspaceDown,\n            niri_ipc::Action::MoveWorkspaceUp {} => Self::MoveWorkspaceUp,\n            niri_ipc::Action::SetWorkspaceName {\n                name,\n                workspace: None,\n            } => Self::SetWorkspaceName(name),\n            niri_ipc::Action::SetWorkspaceName {\n                name,\n                workspace: Some(reference),\n            } => Self::SetWorkspaceNameByRef {\n                name,\n                reference: WorkspaceReference::from(reference),\n            },\n            niri_ipc::Action::UnsetWorkspaceName { reference: None } => Self::UnsetWorkspaceName,\n            niri_ipc::Action::UnsetWorkspaceName {\n                reference: Some(reference),\n            } => Self::UnsetWorkSpaceNameByRef(WorkspaceReference::from(reference)),\n            niri_ipc::Action::FocusMonitorLeft {} => Self::FocusMonitorLeft,\n            niri_ipc::Action::FocusMonitorRight {} => Self::FocusMonitorRight,\n            niri_ipc::Action::FocusMonitorDown {} => Self::FocusMonitorDown,\n            niri_ipc::Action::FocusMonitorUp {} => Self::FocusMonitorUp,\n            niri_ipc::Action::FocusMonitorPrevious {} => Self::FocusMonitorPrevious,\n            niri_ipc::Action::FocusMonitorNext {} => Self::FocusMonitorNext,\n            niri_ipc::Action::FocusMonitor { output } => Self::FocusMonitor(output),\n            niri_ipc::Action::MoveWindowToMonitorLeft {} => Self::MoveWindowToMonitorLeft,\n            niri_ipc::Action::MoveWindowToMonitorRight {} => Self::MoveWindowToMonitorRight,\n            niri_ipc::Action::MoveWindowToMonitorDown {} => Self::MoveWindowToMonitorDown,\n            niri_ipc::Action::MoveWindowToMonitorUp {} => Self::MoveWindowToMonitorUp,\n            niri_ipc::Action::MoveWindowToMonitorPrevious {} => Self::MoveWindowToMonitorPrevious,\n            niri_ipc::Action::MoveWindowToMonitorNext {} => Self::MoveWindowToMonitorNext,\n            niri_ipc::Action::MoveWindowToMonitor { id: None, output } => {\n                Self::MoveWindowToMonitor(output)\n            }\n            niri_ipc::Action::MoveWindowToMonitor {\n                id: Some(id),\n                output,\n            } => Self::MoveWindowToMonitorById { id, output },\n            niri_ipc::Action::MoveColumnToMonitorLeft {} => Self::MoveColumnToMonitorLeft,\n            niri_ipc::Action::MoveColumnToMonitorRight {} => Self::MoveColumnToMonitorRight,\n            niri_ipc::Action::MoveColumnToMonitorDown {} => Self::MoveColumnToMonitorDown,\n            niri_ipc::Action::MoveColumnToMonitorUp {} => Self::MoveColumnToMonitorUp,\n            niri_ipc::Action::MoveColumnToMonitorPrevious {} => Self::MoveColumnToMonitorPrevious,\n            niri_ipc::Action::MoveColumnToMonitorNext {} => Self::MoveColumnToMonitorNext,\n            niri_ipc::Action::MoveColumnToMonitor { output } => Self::MoveColumnToMonitor(output),\n            niri_ipc::Action::SetWindowWidth { id: None, change } => Self::SetWindowWidth(change),\n            niri_ipc::Action::SetWindowWidth {\n                id: Some(id),\n                change,\n            } => Self::SetWindowWidthById { id, change },\n            niri_ipc::Action::SetWindowHeight { id: None, change } => Self::SetWindowHeight(change),\n            niri_ipc::Action::SetWindowHeight {\n                id: Some(id),\n                change,\n            } => Self::SetWindowHeightById { id, change },\n            niri_ipc::Action::ResetWindowHeight { id: None } => Self::ResetWindowHeight,\n            niri_ipc::Action::ResetWindowHeight { id: Some(id) } => Self::ResetWindowHeightById(id),\n            niri_ipc::Action::SwitchPresetColumnWidth {} => Self::SwitchPresetColumnWidth,\n            niri_ipc::Action::SwitchPresetColumnWidthBack {} => Self::SwitchPresetColumnWidthBack,\n            niri_ipc::Action::SwitchPresetWindowWidth { id: None } => Self::SwitchPresetWindowWidth,\n            niri_ipc::Action::SwitchPresetWindowWidthBack { id: None } => {\n                Self::SwitchPresetWindowWidthBack\n            }\n            niri_ipc::Action::SwitchPresetWindowWidth { id: Some(id) } => {\n                Self::SwitchPresetWindowWidthById(id)\n            }\n            niri_ipc::Action::SwitchPresetWindowWidthBack { id: Some(id) } => {\n                Self::SwitchPresetWindowWidthBackById(id)\n            }\n            niri_ipc::Action::SwitchPresetWindowHeight { id: None } => {\n                Self::SwitchPresetWindowHeight\n            }\n            niri_ipc::Action::SwitchPresetWindowHeightBack { id: None } => {\n                Self::SwitchPresetWindowHeightBack\n            }\n            niri_ipc::Action::SwitchPresetWindowHeight { id: Some(id) } => {\n                Self::SwitchPresetWindowHeightById(id)\n            }\n            niri_ipc::Action::SwitchPresetWindowHeightBack { id: Some(id) } => {\n                Self::SwitchPresetWindowHeightBackById(id)\n            }\n            niri_ipc::Action::MaximizeColumn {} => Self::MaximizeColumn,\n            niri_ipc::Action::MaximizeWindowToEdges { id: None } => Self::MaximizeWindowToEdges,\n            niri_ipc::Action::MaximizeWindowToEdges { id: Some(id) } => {\n                Self::MaximizeWindowToEdgesById(id)\n            }\n            niri_ipc::Action::SetColumnWidth { change } => Self::SetColumnWidth(change),\n            niri_ipc::Action::ExpandColumnToAvailableWidth {} => Self::ExpandColumnToAvailableWidth,\n            niri_ipc::Action::SwitchLayout { layout } => Self::SwitchLayout(layout),\n            niri_ipc::Action::ShowHotkeyOverlay {} => Self::ShowHotkeyOverlay,\n            niri_ipc::Action::MoveWorkspaceToMonitorLeft {} => Self::MoveWorkspaceToMonitorLeft,\n            niri_ipc::Action::MoveWorkspaceToMonitorRight {} => Self::MoveWorkspaceToMonitorRight,\n            niri_ipc::Action::MoveWorkspaceToMonitorDown {} => Self::MoveWorkspaceToMonitorDown,\n            niri_ipc::Action::MoveWorkspaceToMonitorUp {} => Self::MoveWorkspaceToMonitorUp,\n            niri_ipc::Action::MoveWorkspaceToMonitorPrevious {} => {\n                Self::MoveWorkspaceToMonitorPrevious\n            }\n            niri_ipc::Action::MoveWorkspaceToIndex {\n                index,\n                reference: Some(reference),\n            } => Self::MoveWorkspaceToIndexByRef {\n                new_idx: index,\n                reference: WorkspaceReference::from(reference),\n            },\n            niri_ipc::Action::MoveWorkspaceToIndex {\n                index,\n                reference: None,\n            } => Self::MoveWorkspaceToIndex(index),\n            niri_ipc::Action::MoveWorkspaceToMonitor {\n                output,\n                reference: Some(reference),\n            } => Self::MoveWorkspaceToMonitorByRef {\n                output_name: output,\n                reference: WorkspaceReference::from(reference),\n            },\n            niri_ipc::Action::MoveWorkspaceToMonitor {\n                output,\n                reference: None,\n            } => Self::MoveWorkspaceToMonitor(output),\n            niri_ipc::Action::MoveWorkspaceToMonitorNext {} => Self::MoveWorkspaceToMonitorNext,\n            niri_ipc::Action::ToggleDebugTint {} => Self::ToggleDebugTint,\n            niri_ipc::Action::DebugToggleOpaqueRegions {} => Self::DebugToggleOpaqueRegions,\n            niri_ipc::Action::DebugToggleDamage {} => Self::DebugToggleDamage,\n            niri_ipc::Action::ToggleWindowFloating { id: None } => Self::ToggleWindowFloating,\n            niri_ipc::Action::ToggleWindowFloating { id: Some(id) } => {\n                Self::ToggleWindowFloatingById(id)\n            }\n            niri_ipc::Action::MoveWindowToFloating { id: None } => Self::MoveWindowToFloating,\n            niri_ipc::Action::MoveWindowToFloating { id: Some(id) } => {\n                Self::MoveWindowToFloatingById(id)\n            }\n            niri_ipc::Action::MoveWindowToTiling { id: None } => Self::MoveWindowToTiling,\n            niri_ipc::Action::MoveWindowToTiling { id: Some(id) } => {\n                Self::MoveWindowToTilingById(id)\n            }\n            niri_ipc::Action::FocusFloating {} => Self::FocusFloating,\n            niri_ipc::Action::FocusTiling {} => Self::FocusTiling,\n            niri_ipc::Action::SwitchFocusBetweenFloatingAndTiling {} => {\n                Self::SwitchFocusBetweenFloatingAndTiling\n            }\n            niri_ipc::Action::MoveFloatingWindow { id, x, y } => {\n                Self::MoveFloatingWindowById { id, x, y }\n            }\n            niri_ipc::Action::ToggleWindowRuleOpacity { id: None } => Self::ToggleWindowRuleOpacity,\n            niri_ipc::Action::ToggleWindowRuleOpacity { id: Some(id) } => {\n                Self::ToggleWindowRuleOpacityById(id)\n            }\n            niri_ipc::Action::SetDynamicCastWindow { id: None } => Self::SetDynamicCastWindow,\n            niri_ipc::Action::SetDynamicCastWindow { id: Some(id) } => {\n                Self::SetDynamicCastWindowById(id)\n            }\n            niri_ipc::Action::SetDynamicCastMonitor { output } => {\n                Self::SetDynamicCastMonitor(output)\n            }\n            niri_ipc::Action::ClearDynamicCastTarget {} => Self::ClearDynamicCastTarget,\n            niri_ipc::Action::StopCast { session_id } => Self::StopCast(session_id),\n            niri_ipc::Action::ToggleOverview {} => Self::ToggleOverview,\n            niri_ipc::Action::OpenOverview {} => Self::OpenOverview,\n            niri_ipc::Action::CloseOverview {} => Self::CloseOverview,\n            niri_ipc::Action::ToggleWindowUrgent { id } => Self::ToggleWindowUrgent(id),\n            niri_ipc::Action::SetWindowUrgent { id } => Self::SetWindowUrgent(id),\n            niri_ipc::Action::UnsetWindowUrgent { id } => Self::UnsetWindowUrgent(id),\n            niri_ipc::Action::LoadConfigFile { path } => Self::LoadConfigFile(path),\n        }\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Clone)]\npub enum WorkspaceReference {\n    Id(u64),\n    Index(u8),\n    Name(String),\n}\n\nimpl From<WorkspaceReferenceArg> for WorkspaceReference {\n    fn from(reference: WorkspaceReferenceArg) -> WorkspaceReference {\n        match reference {\n            WorkspaceReferenceArg::Id(id) => Self::Id(id),\n            WorkspaceReferenceArg::Index(i) => Self::Index(i),\n            WorkspaceReferenceArg::Name(n) => Self::Name(n),\n        }\n    }\n}\n\nimpl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceReference {\n    fn type_check(\n        type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) {\n        if let Some(type_name) = &type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n    }\n\n    fn raw_decode(\n        val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<WorkspaceReference, DecodeError<S>> {\n        match &**val {\n            knuffel::ast::Literal::String(ref s) => Ok(WorkspaceReference::Name(s.clone().into())),\n            knuffel::ast::Literal::Int(ref value) => match value.try_into() {\n                Ok(v) => Ok(WorkspaceReference::Index(v)),\n                Err(e) => {\n                    ctx.emit_error(DecodeError::conversion(val, e));\n                    Ok(WorkspaceReference::Index(0))\n                }\n            },\n            _ => {\n                ctx.emit_error(DecodeError::unsupported(\n                    val,\n                    \"Unsupported value, only numbers and strings are recognized\",\n                ));\n                Ok(WorkspaceReference::Index(0))\n            }\n        }\n    }\n}\n\nimpl<S> knuffel::Decode<S> for Binds\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        expect_only_children(node, ctx);\n\n        let mut seen_keys = HashSet::new();\n\n        let mut binds = Vec::new();\n\n        for child in node.children() {\n            match Bind::decode_node(child, ctx) {\n                Err(e) => {\n                    ctx.emit_error(e);\n                }\n                Ok(bind) => {\n                    if seen_keys.insert(bind.key) {\n                        binds.push(bind);\n                    } else {\n                        // ideally, this error should point to the previous instance of this keybind\n                        //\n                        // i (sodiboo) have tried to implement this in various ways:\n                        // miette!(), #[derive(Diagnostic)]\n                        // DecodeError::Custom, DecodeError::Conversion\n                        // nothing seems to work, and i suspect it's not possible.\n                        //\n                        // DecodeError is fairly restrictive.\n                        // even DecodeError::Custom just wraps a std::error::Error\n                        // and this erases all rich information from miette. (why???)\n                        //\n                        // why does knuffel do this?\n                        // from what i can tell, it doesn't even use DecodeError for much.\n                        // it only ever converts them to a Report anyways!\n                        // https://github.com/tailhook/knuffel/blob/c44c6b0c0f31ea6d1174d5d2ed41064922ea44ca/src/wrappers.rs#L55-L58\n                        //\n                        // besides like, allowing downstream users (such as us!)\n                        // to match on parse failure, i don't understand why\n                        // it doesn't just use a generic error type\n                        //\n                        // even the matching isn't consistent,\n                        // because errors can also be omitted as ctx.emit_error.\n                        // why does *that one* especially, require a DecodeError?\n                        //\n                        // anyways if you can make it format nicely, definitely do fix this\n                        ctx.emit_error(DecodeError::unexpected(\n                            &child.node_name,\n                            \"keybind\",\n                            \"duplicate keybind\",\n                        ));\n                    }\n                }\n            }\n        }\n\n        Ok(Self(binds))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for Bind\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        if let Some(type_name) = &node.type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n\n        for val in node.arguments.iter() {\n            ctx.emit_error(DecodeError::unexpected(\n                &val.literal,\n                \"argument\",\n                \"no arguments expected for this node\",\n            ));\n        }\n\n        let key = node\n            .node_name\n            .parse::<Key>()\n            .map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err(\"invalid keybind\")))?;\n\n        let mut repeat = true;\n        let mut cooldown = None;\n        let mut allow_when_locked = false;\n        let mut allow_when_locked_node = None;\n        let mut allow_inhibiting = true;\n        let mut hotkey_overlay_title = None;\n        for (name, val) in &node.properties {\n            match &***name {\n                \"repeat\" => {\n                    repeat = knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                }\n                \"cooldown-ms\" => {\n                    cooldown = Some(Duration::from_millis(\n                        knuffel::traits::DecodeScalar::decode(val, ctx)?,\n                    ));\n                }\n                \"allow-when-locked\" => {\n                    allow_when_locked = knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                    allow_when_locked_node = Some(name);\n                }\n                \"allow-inhibiting\" => {\n                    allow_inhibiting = knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                }\n                \"hotkey-overlay-title\" => {\n                    hotkey_overlay_title = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);\n                }\n                name_str => {\n                    ctx.emit_error(DecodeError::unexpected(\n                        name,\n                        \"property\",\n                        format!(\"unexpected property `{}`\", name_str.escape_default()),\n                    ));\n                }\n            }\n        }\n\n        let mut children = node.children();\n\n        // If the action is invalid but the key is fine, we still want to return something.\n        // That way, the parent can handle the existence of duplicate keybinds,\n        // even if their contents are not valid.\n        let dummy = Self {\n            key,\n            action: Action::Spawn(vec![]),\n            repeat: true,\n            cooldown: None,\n            allow_when_locked: false,\n            allow_inhibiting: true,\n            hotkey_overlay_title: None,\n        };\n\n        if let Some(child) = children.next() {\n            for unwanted_child in children {\n                ctx.emit_error(DecodeError::unexpected(\n                    unwanted_child,\n                    \"node\",\n                    \"only one action is allowed per keybind\",\n                ));\n            }\n            match Action::decode_node(child, ctx) {\n                Ok(action) => {\n                    if !matches!(action, Action::Spawn(_) | Action::SpawnSh(_)) {\n                        if let Some(node) = allow_when_locked_node {\n                            ctx.emit_error(DecodeError::unexpected(\n                                node,\n                                \"property\",\n                                \"allow-when-locked can only be set on spawn binds\",\n                            ));\n                        }\n                    }\n\n                    // The toggle-inhibit action must always be uninhibitable.\n                    // Otherwise, it would be impossible to trigger it.\n                    if matches!(action, Action::ToggleKeyboardShortcutsInhibit) {\n                        allow_inhibiting = false;\n                    }\n\n                    Ok(Self {\n                        key,\n                        action,\n                        repeat,\n                        cooldown,\n                        allow_when_locked,\n                        allow_inhibiting,\n                        hotkey_overlay_title,\n                    })\n                }\n                Err(e) => {\n                    ctx.emit_error(e);\n                    Ok(dummy)\n                }\n            }\n        } else {\n            ctx.emit_error(DecodeError::missing(\n                node,\n                \"expected an action for this keybind\",\n            ));\n            Ok(dummy)\n        }\n    }\n}\n\nimpl FromStr for Key {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut modifiers = Modifiers::empty();\n\n        let mut split = s.split('+');\n        let key = split.next_back().unwrap();\n\n        for part in split {\n            let part = part.trim();\n            if part.eq_ignore_ascii_case(\"mod\") {\n                modifiers |= Modifiers::COMPOSITOR\n            } else if part.eq_ignore_ascii_case(\"ctrl\") || part.eq_ignore_ascii_case(\"control\") {\n                modifiers |= Modifiers::CTRL;\n            } else if part.eq_ignore_ascii_case(\"shift\") {\n                modifiers |= Modifiers::SHIFT;\n            } else if part.eq_ignore_ascii_case(\"alt\") {\n                modifiers |= Modifiers::ALT;\n            } else if part.eq_ignore_ascii_case(\"super\") || part.eq_ignore_ascii_case(\"win\") {\n                modifiers |= Modifiers::SUPER;\n            } else if part.eq_ignore_ascii_case(\"iso_level3_shift\")\n                || part.eq_ignore_ascii_case(\"mod5\")\n            {\n                modifiers |= Modifiers::ISO_LEVEL3_SHIFT;\n            } else if part.eq_ignore_ascii_case(\"iso_level5_shift\")\n                || part.eq_ignore_ascii_case(\"mod3\")\n            {\n                modifiers |= Modifiers::ISO_LEVEL5_SHIFT;\n            } else {\n                return Err(miette!(\"invalid modifier: {part}\"));\n            }\n        }\n\n        let trigger = if key.eq_ignore_ascii_case(\"MouseLeft\") {\n            Trigger::MouseLeft\n        } else if key.eq_ignore_ascii_case(\"MouseRight\") {\n            Trigger::MouseRight\n        } else if key.eq_ignore_ascii_case(\"MouseMiddle\") {\n            Trigger::MouseMiddle\n        } else if key.eq_ignore_ascii_case(\"MouseBack\") {\n            Trigger::MouseBack\n        } else if key.eq_ignore_ascii_case(\"MouseForward\") {\n            Trigger::MouseForward\n        } else if key.eq_ignore_ascii_case(\"WheelScrollDown\") {\n            Trigger::WheelScrollDown\n        } else if key.eq_ignore_ascii_case(\"WheelScrollUp\") {\n            Trigger::WheelScrollUp\n        } else if key.eq_ignore_ascii_case(\"WheelScrollLeft\") {\n            Trigger::WheelScrollLeft\n        } else if key.eq_ignore_ascii_case(\"WheelScrollRight\") {\n            Trigger::WheelScrollRight\n        } else if key.eq_ignore_ascii_case(\"TouchpadScrollDown\") {\n            Trigger::TouchpadScrollDown\n        } else if key.eq_ignore_ascii_case(\"TouchpadScrollUp\") {\n            Trigger::TouchpadScrollUp\n        } else if key.eq_ignore_ascii_case(\"TouchpadScrollLeft\") {\n            Trigger::TouchpadScrollLeft\n        } else if key.eq_ignore_ascii_case(\"TouchpadScrollRight\") {\n            Trigger::TouchpadScrollRight\n        } else {\n            let mut keysym = keysym_from_name(key, KEYSYM_CASE_INSENSITIVE);\n            // The keyboard event handling code can receive either\n            // XF86ScreenSaver or XF86Screensaver, because there is no\n            // case mapping defined between these keysyms. If we just\n            // use the case-insensitive version of keysym_from_name it\n            // is not possible to bind the uppercase version, because the\n            // case-insensitive match prefers the lowercase version when\n            // there is a choice.\n            //\n            // Therefore, when we match this key with the initial\n            // case-insensitive match we try a further case-sensitive match\n            // (so that either key can be bound). If that fails, we change\n            // to the uppercase version because:\n            //\n            // - A comment in xkb_keysym_from_name (in libxkbcommon) tells us that the uppercase\n            //   version is the \"best\" of the two. [0]\n            // - The xkbcommon crate only has a constant for ScreenSaver. [1]\n            //\n            // [0]: https://github.com/xkbcommon/libxkbcommon/blob/45a118d5325b051343b4b174f60c1434196fa7d4/src/keysym.c#L276\n            // [1]: https://docs.rs/xkbcommon/latest/xkbcommon/xkb/keysyms/index.html#:~:text=KEY%5FXF86ScreenSaver\n            //\n            // See https://github.com/niri-wm/niri/issues/1969\n            if keysym == Keysym::XF86_Screensaver {\n                keysym = keysym_from_name(key, KEYSYM_NO_FLAGS);\n                if keysym.raw() == KEY_NoSymbol {\n                    keysym = Keysym::XF86_ScreenSaver;\n                }\n            }\n            if keysym.raw() == KEY_NoSymbol {\n                return Err(miette!(\"invalid key: {key}\"));\n            }\n            Trigger::Keysym(keysym)\n        };\n\n        Ok(Key { trigger, modifiers })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn parse_xf86_screensaver() {\n        assert_eq!(\n            \"XF86ScreenSaver\".parse::<Key>().unwrap(),\n            Key {\n                trigger: Trigger::Keysym(Keysym::XF86_ScreenSaver),\n                modifiers: Modifiers::empty(),\n            },\n        );\n        assert_eq!(\n            \"XF86Screensaver\".parse::<Key>().unwrap(),\n            Key {\n                trigger: Trigger::Keysym(Keysym::XF86_Screensaver),\n                modifiers: Modifiers::empty(),\n            }\n        );\n        assert_eq!(\n            \"xf86screensaver\".parse::<Key>().unwrap(),\n            Key {\n                trigger: Trigger::Keysym(Keysym::XF86_ScreenSaver),\n                modifiers: Modifiers::empty(),\n            }\n        );\n    }\n\n    #[test]\n    fn parse_iso_level_shifts() {\n        assert_eq!(\n            \"ISO_Level3_Shift+A\".parse::<Key>().unwrap(),\n            Key {\n                trigger: Trigger::Keysym(Keysym::a),\n                modifiers: Modifiers::ISO_LEVEL3_SHIFT\n            },\n        );\n        assert_eq!(\n            \"Mod5+A\".parse::<Key>().unwrap(),\n            Key {\n                trigger: Trigger::Keysym(Keysym::a),\n                modifiers: Modifiers::ISO_LEVEL3_SHIFT\n            },\n        );\n\n        assert_eq!(\n            \"ISO_Level5_Shift+A\".parse::<Key>().unwrap(),\n            Key {\n                trigger: Trigger::Keysym(Keysym::a),\n                modifiers: Modifiers::ISO_LEVEL5_SHIFT\n            },\n        );\n        assert_eq!(\n            \"Mod3+A\".parse::<Key>().unwrap(),\n            Key {\n                trigger: Trigger::Keysym(Keysym::a),\n                modifiers: Modifiers::ISO_LEVEL5_SHIFT\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "niri-config/src/debug.rs",
    "content": "use std::path::PathBuf;\n\nuse crate::utils::{Flag, MergeWith};\n\n#[derive(Debug, Default, PartialEq)]\npub struct Debug {\n    pub preview_render: Option<PreviewRender>,\n    pub dbus_interfaces_in_non_session_instances: bool,\n    pub wait_for_frame_completion_before_queueing: bool,\n    pub enable_overlay_planes: bool,\n    pub disable_cursor_plane: bool,\n    pub disable_direct_scanout: bool,\n    pub keep_max_bpc_unchanged: bool,\n    pub restrict_primary_scanout_to_matching_format: bool,\n    pub force_disable_connectors_on_resume: bool,\n    pub render_drm_device: Option<PathBuf>,\n    pub ignored_drm_devices: Vec<PathBuf>,\n    pub force_pipewire_invalid_modifier: bool,\n    pub emulate_zero_presentation_time: bool,\n    pub disable_resize_throttling: bool,\n    pub disable_transactions: bool,\n    pub keep_laptop_panel_on_when_lid_is_closed: bool,\n    pub disable_monitor_names: bool,\n    pub strict_new_window_focus_policy: bool,\n    pub honor_xdg_activation_with_invalid_serial: bool,\n    pub deactivate_unfocused_windows: bool,\n    pub skip_cursor_only_updates_during_vrr: bool,\n}\n\n#[derive(knuffel::Decode, Debug, Default, PartialEq)]\npub struct DebugPart {\n    #[knuffel(child, unwrap(argument))]\n    pub preview_render: Option<PreviewRender>,\n    #[knuffel(child)]\n    pub dbus_interfaces_in_non_session_instances: Option<Flag>,\n    #[knuffel(child)]\n    pub wait_for_frame_completion_before_queueing: Option<Flag>,\n    #[knuffel(child)]\n    pub enable_overlay_planes: Option<Flag>,\n    #[knuffel(child)]\n    pub disable_cursor_plane: Option<Flag>,\n    #[knuffel(child)]\n    pub disable_direct_scanout: Option<Flag>,\n    #[knuffel(child)]\n    pub keep_max_bpc_unchanged: Option<Flag>,\n    #[knuffel(child)]\n    pub restrict_primary_scanout_to_matching_format: Option<Flag>,\n    #[knuffel(child)]\n    pub force_disable_connectors_on_resume: Option<Flag>,\n    #[knuffel(child, unwrap(argument))]\n    pub render_drm_device: Option<PathBuf>,\n    #[knuffel(children(name = \"ignore-drm-device\"), unwrap(argument))]\n    pub ignored_drm_devices: Vec<PathBuf>,\n    #[knuffel(child)]\n    pub force_pipewire_invalid_modifier: Option<Flag>,\n    #[knuffel(child)]\n    pub emulate_zero_presentation_time: Option<Flag>,\n    #[knuffel(child)]\n    pub disable_resize_throttling: Option<Flag>,\n    #[knuffel(child)]\n    pub disable_transactions: Option<Flag>,\n    #[knuffel(child)]\n    pub keep_laptop_panel_on_when_lid_is_closed: Option<Flag>,\n    #[knuffel(child)]\n    pub disable_monitor_names: Option<Flag>,\n    #[knuffel(child)]\n    pub strict_new_window_focus_policy: Option<Flag>,\n    #[knuffel(child)]\n    pub honor_xdg_activation_with_invalid_serial: Option<Flag>,\n    #[knuffel(child)]\n    pub deactivate_unfocused_windows: Option<Flag>,\n    #[knuffel(child)]\n    pub skip_cursor_only_updates_during_vrr: Option<Flag>,\n}\n\nimpl MergeWith<DebugPart> for Debug {\n    fn merge_with(&mut self, part: &DebugPart) {\n        merge!(\n            (self, part),\n            dbus_interfaces_in_non_session_instances,\n            wait_for_frame_completion_before_queueing,\n            enable_overlay_planes,\n            disable_cursor_plane,\n            disable_direct_scanout,\n            keep_max_bpc_unchanged,\n            restrict_primary_scanout_to_matching_format,\n            force_disable_connectors_on_resume,\n            force_pipewire_invalid_modifier,\n            emulate_zero_presentation_time,\n            disable_resize_throttling,\n            disable_transactions,\n            keep_laptop_panel_on_when_lid_is_closed,\n            disable_monitor_names,\n            strict_new_window_focus_policy,\n            honor_xdg_activation_with_invalid_serial,\n            deactivate_unfocused_windows,\n            skip_cursor_only_updates_during_vrr,\n        );\n\n        merge_clone_opt!((self, part), preview_render, render_drm_device);\n\n        self.ignored_drm_devices\n            .extend(part.ignored_drm_devices.iter().cloned());\n    }\n}\n\n#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq, Eq)]\npub enum PreviewRender {\n    Screencast,\n    ScreenCapture,\n}\n"
  },
  {
    "path": "niri-config/src/error.rs",
    "content": "use std::error::Error;\nuse std::fmt;\nuse std::path::PathBuf;\n\nuse miette::Diagnostic;\n\n#[derive(Debug)]\npub struct ConfigParseResult<T, E> {\n    pub config: Result<T, E>,\n\n    // We always try to return includes for the file watcher.\n    //\n    // If the main config is valid, but an included file fails to parse, config will be an Err(),\n    // but includes will still be filled, so that fixing just the included file is enough to\n    // trigger a reload.\n    pub includes: Vec<PathBuf>,\n}\n\n/// Error type that chains main errors with include errors.\n///\n/// Allows miette's Report formatting to have main + include errors all in one.\n#[derive(Debug)]\npub struct ConfigIncludeError {\n    pub main: knuffel::Error,\n    pub includes: Vec<knuffel::Error>,\n}\n\nimpl<T, E> ConfigParseResult<T, E> {\n    pub fn from_err(err: E) -> Self {\n        Self {\n            config: Err(err),\n            includes: Vec::new(),\n        }\n    }\n\n    pub fn map_config_res<U, V>(\n        self,\n        f: impl FnOnce(Result<T, E>) -> Result<U, V>,\n    ) -> ConfigParseResult<U, V> {\n        ConfigParseResult {\n            config: f(self.config),\n            includes: self.includes,\n        }\n    }\n}\n\nimpl fmt::Display for ConfigIncludeError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(&self.main, f)\n    }\n}\n\nimpl Error for ConfigIncludeError {\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        self.main.source()\n    }\n}\n\nimpl Diagnostic for ConfigIncludeError {\n    fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {\n        self.main.code()\n    }\n\n    fn severity(&self) -> Option<miette::Severity> {\n        self.main.severity()\n    }\n\n    fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {\n        self.main.help()\n    }\n\n    fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {\n        self.main.url()\n    }\n\n    fn source_code(&self) -> Option<&dyn miette::SourceCode> {\n        self.main.source_code()\n    }\n\n    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {\n        self.main.labels()\n    }\n\n    fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {\n        self.main.diagnostic_source()\n    }\n\n    fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {\n        let main_related = self.main.related();\n        let includes_iter = self.includes.iter().map(|err| err as &'a dyn Diagnostic);\n\n        let iter: Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a> = match main_related {\n            Some(main) => Box::new(main.chain(includes_iter)),\n            None => Box::new(includes_iter),\n        };\n\n        Some(iter)\n    }\n}\n"
  },
  {
    "path": "niri-config/src/gestures.rs",
    "content": "use crate::utils::MergeWith;\nuse crate::FloatOrInt;\n\n#[derive(Debug, Default, Clone, Copy, PartialEq)]\npub struct Gestures {\n    pub dnd_edge_view_scroll: DndEdgeViewScroll,\n    pub dnd_edge_workspace_switch: DndEdgeWorkspaceSwitch,\n    pub hot_corners: HotCorners,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct GesturesPart {\n    #[knuffel(child)]\n    pub dnd_edge_view_scroll: Option<DndEdgeViewScrollPart>,\n    #[knuffel(child)]\n    pub dnd_edge_workspace_switch: Option<DndEdgeWorkspaceSwitchPart>,\n    #[knuffel(child)]\n    pub hot_corners: Option<HotCorners>,\n}\n\nimpl MergeWith<GesturesPart> for Gestures {\n    fn merge_with(&mut self, part: &GesturesPart) {\n        merge!(\n            (self, part),\n            dnd_edge_view_scroll,\n            dnd_edge_workspace_switch,\n        );\n        merge_clone!((self, part), hot_corners);\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct DndEdgeViewScroll {\n    pub trigger_width: f64,\n    pub delay_ms: u16,\n    pub max_speed: f64,\n}\n\nimpl Default for DndEdgeViewScroll {\n    fn default() -> Self {\n        Self {\n            trigger_width: 30., // Taken from GTK 4.\n            delay_ms: 100,\n            max_speed: 1500.,\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct DndEdgeViewScrollPart {\n    #[knuffel(child, unwrap(argument))]\n    pub trigger_width: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child, unwrap(argument))]\n    pub delay_ms: Option<u16>,\n    #[knuffel(child, unwrap(argument))]\n    pub max_speed: Option<FloatOrInt<0, 1_000_000>>,\n}\n\nimpl MergeWith<DndEdgeViewScrollPart> for DndEdgeViewScroll {\n    fn merge_with(&mut self, part: &DndEdgeViewScrollPart) {\n        merge!((self, part), trigger_width, max_speed);\n        merge_clone!((self, part), delay_ms);\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct DndEdgeWorkspaceSwitch {\n    pub trigger_height: f64,\n    pub delay_ms: u16,\n    pub max_speed: f64,\n}\n\nimpl Default for DndEdgeWorkspaceSwitch {\n    fn default() -> Self {\n        Self {\n            trigger_height: 50.,\n            delay_ms: 100,\n            max_speed: 1500.,\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct DndEdgeWorkspaceSwitchPart {\n    #[knuffel(child, unwrap(argument))]\n    pub trigger_height: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child, unwrap(argument))]\n    pub delay_ms: Option<u16>,\n    #[knuffel(child, unwrap(argument))]\n    pub max_speed: Option<FloatOrInt<0, 1_000_000>>,\n}\n\nimpl MergeWith<DndEdgeWorkspaceSwitchPart> for DndEdgeWorkspaceSwitch {\n    fn merge_with(&mut self, part: &DndEdgeWorkspaceSwitchPart) {\n        merge!((self, part), trigger_height, max_speed);\n        merge_clone!((self, part), delay_ms);\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct HotCorners {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub top_left: bool,\n    #[knuffel(child)]\n    pub top_right: bool,\n    #[knuffel(child)]\n    pub bottom_left: bool,\n    #[knuffel(child)]\n    pub bottom_right: bool,\n}\n"
  },
  {
    "path": "niri-config/src/input.rs",
    "content": "use std::str::FromStr;\n\nuse miette::miette;\nuse smithay::input::keyboard::XkbConfig;\nuse smithay::reexports::input;\n\nuse crate::binds::Modifiers;\nuse crate::utils::{Flag, MergeWith, Percent};\nuse crate::FloatOrInt;\n\n#[derive(Debug, Default, PartialEq)]\npub struct Input {\n    pub keyboard: Keyboard,\n    pub touchpad: Touchpad,\n    pub mouse: Mouse,\n    pub trackpoint: Trackpoint,\n    pub trackball: Trackball,\n    pub tablet: Tablet,\n    pub touch: Touch,\n    pub disable_power_key_handling: bool,\n    pub warp_mouse_to_focus: Option<WarpMouseToFocus>,\n    pub focus_follows_mouse: Option<FocusFollowsMouse>,\n    pub workspace_auto_back_and_forth: bool,\n    pub mod_key: Option<ModKey>,\n    pub mod_key_nested: Option<ModKey>,\n}\n\n#[derive(knuffel::Decode, Debug, Default, PartialEq)]\npub struct InputPart {\n    #[knuffel(child)]\n    pub keyboard: Option<KeyboardPart>,\n    #[knuffel(child)]\n    pub touchpad: Option<Touchpad>,\n    #[knuffel(child)]\n    pub mouse: Option<Mouse>,\n    #[knuffel(child)]\n    pub trackpoint: Option<Trackpoint>,\n    #[knuffel(child)]\n    pub trackball: Option<Trackball>,\n    #[knuffel(child)]\n    pub tablet: Option<Tablet>,\n    #[knuffel(child)]\n    pub touch: Option<Touch>,\n    #[knuffel(child)]\n    pub disable_power_key_handling: Option<Flag>,\n    #[knuffel(child)]\n    pub warp_mouse_to_focus: Option<WarpMouseToFocus>,\n    #[knuffel(child)]\n    pub focus_follows_mouse: Option<FocusFollowsMouse>,\n    #[knuffel(child)]\n    pub workspace_auto_back_and_forth: Option<Flag>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub mod_key: Option<ModKey>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub mod_key_nested: Option<ModKey>,\n}\n\nimpl MergeWith<InputPart> for Input {\n    fn merge_with(&mut self, part: &InputPart) {\n        merge!(\n            (self, part),\n            keyboard,\n            disable_power_key_handling,\n            workspace_auto_back_and_forth,\n        );\n\n        merge_clone!(\n            (self, part),\n            touchpad,\n            mouse,\n            trackpoint,\n            trackball,\n            tablet,\n            touch,\n        );\n\n        merge_clone_opt!(\n            (self, part),\n            warp_mouse_to_focus,\n            focus_follows_mouse,\n            mod_key,\n            mod_key_nested,\n        );\n    }\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub struct Keyboard {\n    pub xkb: Xkb,\n    pub repeat_delay: u16,\n    pub repeat_rate: u8,\n    pub track_layout: TrackLayout,\n    pub numlock: bool,\n}\n\nimpl Default for Keyboard {\n    fn default() -> Self {\n        Self {\n            xkb: Default::default(),\n            // The defaults were chosen to match wlroots and sway.\n            repeat_delay: 600,\n            repeat_rate: 25,\n            track_layout: Default::default(),\n            numlock: Default::default(),\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, PartialEq, Eq)]\npub struct KeyboardPart {\n    #[knuffel(child)]\n    pub xkb: Option<Xkb>,\n    #[knuffel(child, unwrap(argument))]\n    pub repeat_delay: Option<u16>,\n    #[knuffel(child, unwrap(argument))]\n    pub repeat_rate: Option<u8>,\n    #[knuffel(child, unwrap(argument))]\n    pub track_layout: Option<TrackLayout>,\n    #[knuffel(child)]\n    pub numlock: Option<Flag>,\n}\n\nimpl MergeWith<KeyboardPart> for Keyboard {\n    fn merge_with(&mut self, part: &KeyboardPart) {\n        merge_clone!((self, part), xkb, repeat_delay, repeat_rate, track_layout);\n        merge!((self, part), numlock);\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, PartialEq, Eq, Clone)]\npub struct Xkb {\n    #[knuffel(child, unwrap(argument), default)]\n    pub rules: String,\n    #[knuffel(child, unwrap(argument), default)]\n    pub model: String,\n    #[knuffel(child, unwrap(argument), default)]\n    pub layout: String,\n    #[knuffel(child, unwrap(argument), default)]\n    pub variant: String,\n    #[knuffel(child, unwrap(argument))]\n    pub options: Option<String>,\n    #[knuffel(child, unwrap(argument))]\n    pub file: Option<String>,\n}\n\nimpl Xkb {\n    pub fn to_xkb_config(&self) -> XkbConfig<'_> {\n        XkbConfig {\n            rules: &self.rules,\n            model: &self.model,\n            layout: &self.layout,\n            variant: &self.variant,\n            options: self.options.clone(),\n        }\n    }\n}\n\n#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum TrackLayout {\n    /// The layout change is global.\n    #[default]\n    Global,\n    /// The layout change is window local.\n    Window,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct ScrollFactor {\n    #[knuffel(argument)]\n    pub base: Option<FloatOrInt<0, 100>>,\n    #[knuffel(property)]\n    pub horizontal: Option<FloatOrInt<-100, 100>>,\n    #[knuffel(property)]\n    pub vertical: Option<FloatOrInt<-100, 100>>,\n}\n\nimpl ScrollFactor {\n    pub fn h_v_factors(&self) -> (f64, f64) {\n        let base_value = self.base.map(|f| f.0).unwrap_or(1.0);\n        let h = self.horizontal.map(|f| f.0).unwrap_or(base_value);\n        let v = self.vertical.map(|f| f.0).unwrap_or(base_value);\n        (h, v)\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Touchpad {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub tap: bool,\n    #[knuffel(child)]\n    pub dwt: bool,\n    #[knuffel(child)]\n    pub dwtp: bool,\n    #[knuffel(child, unwrap(argument))]\n    pub drag: Option<bool>,\n    #[knuffel(child)]\n    pub drag_lock: bool,\n    #[knuffel(child)]\n    pub natural_scroll: bool,\n    #[knuffel(child, unwrap(argument, str))]\n    pub click_method: Option<ClickMethod>,\n    #[knuffel(child, unwrap(argument), default)]\n    pub accel_speed: FloatOrInt<-1, 1>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub accel_profile: Option<AccelProfile>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub scroll_method: Option<ScrollMethod>,\n    #[knuffel(child, unwrap(argument))]\n    pub scroll_button: Option<u32>,\n    #[knuffel(child)]\n    pub scroll_button_lock: bool,\n    #[knuffel(child, unwrap(argument, str))]\n    pub tap_button_map: Option<TapButtonMap>,\n    #[knuffel(child)]\n    pub left_handed: bool,\n    #[knuffel(child)]\n    pub disabled_on_external_mouse: bool,\n    #[knuffel(child)]\n    pub middle_emulation: bool,\n    #[knuffel(child)]\n    pub scroll_factor: Option<ScrollFactor>,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Mouse {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub natural_scroll: bool,\n    #[knuffel(child, unwrap(argument), default)]\n    pub accel_speed: FloatOrInt<-1, 1>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub accel_profile: Option<AccelProfile>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub scroll_method: Option<ScrollMethod>,\n    #[knuffel(child, unwrap(argument))]\n    pub scroll_button: Option<u32>,\n    #[knuffel(child)]\n    pub scroll_button_lock: bool,\n    #[knuffel(child)]\n    pub left_handed: bool,\n    #[knuffel(child)]\n    pub middle_emulation: bool,\n    #[knuffel(child)]\n    pub scroll_factor: Option<ScrollFactor>,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Trackpoint {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub natural_scroll: bool,\n    #[knuffel(child, unwrap(argument), default)]\n    pub accel_speed: FloatOrInt<-1, 1>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub accel_profile: Option<AccelProfile>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub scroll_method: Option<ScrollMethod>,\n    #[knuffel(child, unwrap(argument))]\n    pub scroll_button: Option<u32>,\n    #[knuffel(child)]\n    pub scroll_button_lock: bool,\n    #[knuffel(child)]\n    pub left_handed: bool,\n    #[knuffel(child)]\n    pub middle_emulation: bool,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Trackball {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub natural_scroll: bool,\n    #[knuffel(child, unwrap(argument), default)]\n    pub accel_speed: FloatOrInt<-1, 1>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub accel_profile: Option<AccelProfile>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub scroll_method: Option<ScrollMethod>,\n    #[knuffel(child, unwrap(argument))]\n    pub scroll_button: Option<u32>,\n    #[knuffel(child)]\n    pub scroll_button_lock: bool,\n    #[knuffel(child)]\n    pub left_handed: bool,\n    #[knuffel(child)]\n    pub middle_emulation: bool,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ClickMethod {\n    Clickfinger,\n    ButtonAreas,\n}\n\nimpl From<ClickMethod> for input::ClickMethod {\n    fn from(value: ClickMethod) -> Self {\n        match value {\n            ClickMethod::Clickfinger => Self::Clickfinger,\n            ClickMethod::ButtonAreas => Self::ButtonAreas,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum AccelProfile {\n    Adaptive,\n    Flat,\n}\n\nimpl From<AccelProfile> for input::AccelProfile {\n    fn from(value: AccelProfile) -> Self {\n        match value {\n            AccelProfile::Adaptive => Self::Adaptive,\n            AccelProfile::Flat => Self::Flat,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ScrollMethod {\n    NoScroll,\n    TwoFinger,\n    Edge,\n    OnButtonDown,\n}\n\nimpl From<ScrollMethod> for input::ScrollMethod {\n    fn from(value: ScrollMethod) -> Self {\n        match value {\n            ScrollMethod::NoScroll => Self::NoScroll,\n            ScrollMethod::TwoFinger => Self::TwoFinger,\n            ScrollMethod::Edge => Self::Edge,\n            ScrollMethod::OnButtonDown => Self::OnButtonDown,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum TapButtonMap {\n    LeftRightMiddle,\n    LeftMiddleRight,\n}\n\nimpl From<TapButtonMap> for input::TapButtonMap {\n    fn from(value: TapButtonMap) -> Self {\n        match value {\n            TapButtonMap::LeftRightMiddle => Self::LeftRightMiddle,\n            TapButtonMap::LeftMiddleRight => Self::LeftMiddleRight,\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Tablet {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child, unwrap(arguments))]\n    pub calibration_matrix: Option<Vec<f32>>,\n    #[knuffel(child, unwrap(argument))]\n    pub map_to_output: Option<String>,\n    #[knuffel(child)]\n    pub left_handed: bool,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Touch {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child, unwrap(arguments))]\n    pub calibration_matrix: Option<Vec<f32>>,\n    #[knuffel(child, unwrap(argument))]\n    pub map_to_output: Option<String>,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct FocusFollowsMouse {\n    #[knuffel(property, str)]\n    pub max_scroll_amount: Option<Percent>,\n}\n\n#[derive(knuffel::Decode, Debug, PartialEq, Eq, Clone, Copy)]\npub struct WarpMouseToFocus {\n    #[knuffel(property, str)]\n    pub mode: Option<WarpMouseToFocusMode>,\n}\n\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\npub enum WarpMouseToFocusMode {\n    CenterXy,\n    CenterXyAlways,\n}\n\nimpl FromStr for WarpMouseToFocusMode {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"center-xy\" => Ok(Self::CenterXy),\n            \"center-xy-always\" => Ok(Self::CenterXyAlways),\n            _ => Err(miette!(\n                r#\"invalid mode for warp-mouse-to-focus, can be \"center-xy\" or \"center-xy-always\" (or leave unset for separate centering)\"#\n            )),\n        }\n    }\n}\n\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\npub enum ModKey {\n    Ctrl,\n    Shift,\n    Alt,\n    Super,\n    IsoLevel3Shift,\n    IsoLevel5Shift,\n}\n\nimpl ModKey {\n    pub fn to_modifiers(&self) -> Modifiers {\n        match self {\n            ModKey::Ctrl => Modifiers::CTRL,\n            ModKey::Shift => Modifiers::SHIFT,\n            ModKey::Alt => Modifiers::ALT,\n            ModKey::Super => Modifiers::SUPER,\n            ModKey::IsoLevel3Shift => Modifiers::ISO_LEVEL3_SHIFT,\n            ModKey::IsoLevel5Shift => Modifiers::ISO_LEVEL5_SHIFT,\n        }\n    }\n}\n\nimpl FromStr for ModKey {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match &*s.to_ascii_lowercase() {\n            \"ctrl\" | \"control\" => Ok(Self::Ctrl),\n            \"shift\" => Ok(Self::Shift),\n            \"alt\" => Ok(Self::Alt),\n            \"super\" | \"win\" => Ok(Self::Super),\n            \"iso_level3_shift\" | \"mod5\" => Ok(Self::IsoLevel3Shift),\n            \"iso_level5_shift\" | \"mod3\" => Ok(Self::IsoLevel5Shift),\n            _ => Err(miette!(\"invalid Mod key: {s}\")),\n        }\n    }\n}\n\nimpl FromStr for ClickMethod {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"clickfinger\" => Ok(Self::Clickfinger),\n            \"button-areas\" => Ok(Self::ButtonAreas),\n            _ => Err(miette!(\n                r#\"invalid click method, can be \"button-areas\" or \"clickfinger\"\"#\n            )),\n        }\n    }\n}\n\nimpl FromStr for AccelProfile {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"adaptive\" => Ok(Self::Adaptive),\n            \"flat\" => Ok(Self::Flat),\n            _ => Err(miette!(\n                r#\"invalid accel profile, can be \"adaptive\" or \"flat\"\"#\n            )),\n        }\n    }\n}\n\nimpl FromStr for ScrollMethod {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"no-scroll\" => Ok(Self::NoScroll),\n            \"two-finger\" => Ok(Self::TwoFinger),\n            \"edge\" => Ok(Self::Edge),\n            \"on-button-down\" => Ok(Self::OnButtonDown),\n            _ => Err(miette!(\n                r#\"invalid scroll method, can be \"no-scroll\", \"two-finger\", \"edge\", or \"on-button-down\"\"#\n            )),\n        }\n    }\n}\n\nimpl FromStr for TapButtonMap {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"left-right-middle\" => Ok(Self::LeftRightMiddle),\n            \"left-middle-right\" => Ok(Self::LeftMiddleRight),\n            _ => Err(miette!(\n                r#\"invalid tap button map, can be \"left-right-middle\" or \"left-middle-right\"\"#\n            )),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::assert_debug_snapshot;\n\n    use super::*;\n\n    #[track_caller]\n    fn do_parse(text: &str) -> Input {\n        let part = knuffel::parse(\"test.kdl\", text)\n            .map_err(miette::Report::new)\n            .unwrap();\n        Input::from_part(&part)\n    }\n\n    #[test]\n    fn parse_scroll_factor_combined() {\n        // Test combined scroll-factor syntax\n        let parsed = do_parse(\n            r#\"\n            mouse {\n                scroll-factor 2.0\n            }\n            touchpad {\n                scroll-factor 1.5\n            }\n            \"#,\n        );\n\n        assert_debug_snapshot!(parsed.mouse.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: Some(\n                    FloatOrInt(\n                        2.0,\n                    ),\n                ),\n                horizontal: None,\n                vertical: None,\n            },\n        )\n        \"#);\n        assert_debug_snapshot!(parsed.touchpad.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: Some(\n                    FloatOrInt(\n                        1.5,\n                    ),\n                ),\n                horizontal: None,\n                vertical: None,\n            },\n        )\n        \"#);\n    }\n\n    #[test]\n    fn parse_scroll_factor_split() {\n        // Test split horizontal/vertical syntax\n        let parsed = do_parse(\n            r#\"\n            mouse {\n                scroll-factor horizontal=2.0 vertical=-1.0\n            }\n            touchpad {\n                scroll-factor horizontal=-1.5 vertical=0.5\n            }\n            \"#,\n        );\n\n        assert_debug_snapshot!(parsed.mouse.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: None,\n                horizontal: Some(\n                    FloatOrInt(\n                        2.0,\n                    ),\n                ),\n                vertical: Some(\n                    FloatOrInt(\n                        -1.0,\n                    ),\n                ),\n            },\n        )\n        \"#);\n        assert_debug_snapshot!(parsed.touchpad.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: None,\n                horizontal: Some(\n                    FloatOrInt(\n                        -1.5,\n                    ),\n                ),\n                vertical: Some(\n                    FloatOrInt(\n                        0.5,\n                    ),\n                ),\n            },\n        )\n        \"#);\n    }\n\n    #[test]\n    fn parse_scroll_factor_partial() {\n        // Test partial specification (only one axis)\n        let parsed = do_parse(\n            r#\"\n            mouse {\n                scroll-factor horizontal=2.0\n            }\n            touchpad {\n                scroll-factor vertical=-1.5\n            }\n            \"#,\n        );\n\n        assert_debug_snapshot!(parsed.mouse.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: None,\n                horizontal: Some(\n                    FloatOrInt(\n                        2.0,\n                    ),\n                ),\n                vertical: None,\n            },\n        )\n        \"#);\n        assert_debug_snapshot!(parsed.touchpad.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: None,\n                horizontal: None,\n                vertical: Some(\n                    FloatOrInt(\n                        -1.5,\n                    ),\n                ),\n            },\n        )\n        \"#);\n    }\n\n    #[test]\n    fn parse_scroll_factor_mixed() {\n        // Test mixed base + override syntax\n        let parsed = do_parse(\n            r#\"\n            mouse {\n                scroll-factor 2 vertical=-1\n            }\n            touchpad {\n                scroll-factor 1.5 horizontal=3\n            }\n            \"#,\n        );\n\n        assert_debug_snapshot!(parsed.mouse.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: Some(\n                    FloatOrInt(\n                        2.0,\n                    ),\n                ),\n                horizontal: None,\n                vertical: Some(\n                    FloatOrInt(\n                        -1.0,\n                    ),\n                ),\n            },\n        )\n        \"#);\n        assert_debug_snapshot!(parsed.touchpad.scroll_factor, @r#\"\n        Some(\n            ScrollFactor {\n                base: Some(\n                    FloatOrInt(\n                        1.5,\n                    ),\n                ),\n                horizontal: Some(\n                    FloatOrInt(\n                        3.0,\n                    ),\n                ),\n                vertical: None,\n            },\n        )\n        \"#);\n    }\n\n    #[test]\n    fn scroll_factor_h_v_factors() {\n        let sf = ScrollFactor {\n            base: Some(FloatOrInt(2.0)),\n            horizontal: None,\n            vertical: None,\n        };\n        assert_debug_snapshot!(sf.h_v_factors(), @r#\"\n        (\n            2.0,\n            2.0,\n        )\n        \"#);\n\n        let sf = ScrollFactor {\n            base: None,\n            horizontal: Some(FloatOrInt(3.0)),\n            vertical: Some(FloatOrInt(-1.0)),\n        };\n        assert_debug_snapshot!(sf.h_v_factors(), @r#\"\n        (\n            3.0,\n            -1.0,\n        )\n        \"#);\n\n        let sf = ScrollFactor {\n            base: Some(FloatOrInt(2.0)),\n            horizontal: Some(FloatOrInt(1.0)),\n            vertical: None,\n        };\n        assert_debug_snapshot!(sf.h_v_factors(), @r\"\n        (\n            1.0,\n            2.0,\n        )\n        \");\n    }\n}\n"
  },
  {
    "path": "niri-config/src/layer_rule.rs",
    "content": "use crate::appearance::{BlockOutFrom, CornerRadius, ShadowRule};\nuse crate::utils::RegexEq;\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct LayerRule {\n    #[knuffel(children(name = \"match\"))]\n    pub matches: Vec<Match>,\n    #[knuffel(children(name = \"exclude\"))]\n    pub excludes: Vec<Match>,\n\n    #[knuffel(child, unwrap(argument))]\n    pub opacity: Option<f32>,\n    #[knuffel(child, unwrap(argument))]\n    pub block_out_from: Option<BlockOutFrom>,\n    #[knuffel(child, default)]\n    pub shadow: ShadowRule,\n    #[knuffel(child)]\n    pub geometry_corner_radius: Option<CornerRadius>,\n    #[knuffel(child, unwrap(argument))]\n    pub place_within_backdrop: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub baba_is_float: Option<bool>,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Match {\n    #[knuffel(property, str)]\n    pub namespace: Option<RegexEq>,\n    #[knuffel(property)]\n    pub at_startup: Option<bool>,\n}\n"
  },
  {
    "path": "niri-config/src/layout.rs",
    "content": "use knuffel::errors::DecodeError;\nuse niri_ipc::{ColumnDisplay, SizeChange};\n\nuse crate::appearance::{\n    Border, FocusRing, InsertHint, Shadow, TabIndicator, DEFAULT_BACKGROUND_COLOR,\n};\nuse crate::utils::{expect_only_children, Flag, MergeWith};\nuse crate::{BorderRule, Color, FloatOrInt, InsertHintPart, ShadowRule, TabIndicatorPart};\n\n#[derive(Debug, Clone, PartialEq)]\npub struct Layout {\n    pub focus_ring: FocusRing,\n    pub border: Border,\n    pub shadow: Shadow,\n    pub tab_indicator: TabIndicator,\n    pub insert_hint: InsertHint,\n    pub preset_column_widths: Vec<PresetSize>,\n    pub default_column_width: Option<PresetSize>,\n    pub preset_window_heights: Vec<PresetSize>,\n    pub center_focused_column: CenterFocusedColumn,\n    pub always_center_single_column: bool,\n    pub empty_workspace_above_first: bool,\n    pub default_column_display: ColumnDisplay,\n    pub gaps: f64,\n    pub struts: Struts,\n    pub background_color: Color,\n}\n\nimpl Default for Layout {\n    fn default() -> Self {\n        Self {\n            focus_ring: FocusRing::default(),\n            border: Border::default(),\n            shadow: Shadow::default(),\n            tab_indicator: TabIndicator::default(),\n            insert_hint: InsertHint::default(),\n            preset_column_widths: vec![\n                PresetSize::Proportion(1. / 3.),\n                PresetSize::Proportion(0.5),\n                PresetSize::Proportion(2. / 3.),\n            ],\n            default_column_width: Some(PresetSize::Proportion(0.5)),\n            center_focused_column: CenterFocusedColumn::Never,\n            always_center_single_column: false,\n            empty_workspace_above_first: false,\n            default_column_display: ColumnDisplay::Normal,\n            gaps: 16.,\n            struts: Struts::default(),\n            preset_window_heights: vec![\n                PresetSize::Proportion(1. / 3.),\n                PresetSize::Proportion(0.5),\n                PresetSize::Proportion(2. / 3.),\n            ],\n            background_color: DEFAULT_BACKGROUND_COLOR,\n        }\n    }\n}\n\nimpl MergeWith<LayoutPart> for Layout {\n    fn merge_with(&mut self, part: &LayoutPart) {\n        merge!(\n            (self, part),\n            focus_ring,\n            border,\n            shadow,\n            tab_indicator,\n            insert_hint,\n            always_center_single_column,\n            empty_workspace_above_first,\n            gaps,\n        );\n\n        merge_clone!(\n            (self, part),\n            preset_column_widths,\n            preset_window_heights,\n            center_focused_column,\n            default_column_display,\n            struts,\n            background_color,\n        );\n\n        if let Some(x) = part.default_column_width {\n            self.default_column_width = x.0;\n        }\n\n        if self.preset_column_widths.is_empty() {\n            self.preset_column_widths = Layout::default().preset_column_widths;\n        }\n\n        if self.preset_window_heights.is_empty() {\n            self.preset_window_heights = Layout::default().preset_window_heights;\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct LayoutPart {\n    #[knuffel(child)]\n    pub focus_ring: Option<BorderRule>,\n    #[knuffel(child)]\n    pub border: Option<BorderRule>,\n    #[knuffel(child)]\n    pub shadow: Option<ShadowRule>,\n    #[knuffel(child)]\n    pub tab_indicator: Option<TabIndicatorPart>,\n    #[knuffel(child)]\n    pub insert_hint: Option<InsertHintPart>,\n    #[knuffel(child, unwrap(children))]\n    pub preset_column_widths: Option<Vec<PresetSize>>,\n    #[knuffel(child)]\n    pub default_column_width: Option<DefaultPresetSize>,\n    #[knuffel(child, unwrap(children))]\n    pub preset_window_heights: Option<Vec<PresetSize>>,\n    #[knuffel(child, unwrap(argument))]\n    pub center_focused_column: Option<CenterFocusedColumn>,\n    #[knuffel(child)]\n    pub always_center_single_column: Option<Flag>,\n    #[knuffel(child)]\n    pub empty_workspace_above_first: Option<Flag>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub default_column_display: Option<ColumnDisplay>,\n    #[knuffel(child, unwrap(argument))]\n    pub gaps: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child)]\n    pub struts: Option<Struts>,\n    #[knuffel(child)]\n    pub background_color: Option<Color>,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub enum PresetSize {\n    Proportion(#[knuffel(argument)] f64),\n    Fixed(#[knuffel(argument)] i32),\n}\n\nimpl From<PresetSize> for SizeChange {\n    fn from(value: PresetSize) -> Self {\n        match value {\n            PresetSize::Proportion(prop) => SizeChange::SetProportion(prop * 100.),\n            PresetSize::Fixed(fixed) => SizeChange::SetFixed(fixed),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct DefaultPresetSize(pub Option<PresetSize>);\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]\npub struct Struts {\n    #[knuffel(child, unwrap(argument), default)]\n    pub left: FloatOrInt<-65535, 65535>,\n    #[knuffel(child, unwrap(argument), default)]\n    pub right: FloatOrInt<-65535, 65535>,\n    #[knuffel(child, unwrap(argument), default)]\n    pub top: FloatOrInt<-65535, 65535>,\n    #[knuffel(child, unwrap(argument), default)]\n    pub bottom: FloatOrInt<-65535, 65535>,\n}\n\n#[derive(knuffel::DecodeScalar, Debug, Default, PartialEq, Eq, Clone, Copy)]\npub enum CenterFocusedColumn {\n    /// Focusing a column will not center the column.\n    #[default]\n    Never,\n    /// The focused column will always be centered.\n    Always,\n    /// Focusing a column will center it if it doesn't fit on the screen together with the\n    /// previously focused column.\n    OnOverflow,\n}\n\nimpl<S> knuffel::Decode<S> for DefaultPresetSize\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        expect_only_children(node, ctx);\n\n        let mut children = node.children();\n\n        if let Some(child) = children.next() {\n            if let Some(unwanted_child) = children.next() {\n                ctx.emit_error(DecodeError::unexpected(\n                    unwanted_child,\n                    \"node\",\n                    \"expected no more than one child\",\n                ));\n            }\n            PresetSize::decode_node(child, ctx).map(Some).map(Self)\n        } else {\n            Ok(Self(None))\n        }\n    }\n}\n"
  },
  {
    "path": "niri-config/src/lib.rs",
    "content": "//! niri config parsing.\n//!\n//! The config can be constructed from multiple files (includes). To support this, many types are\n//! split into two. For example, `Layout` and `LayoutPart` where `Layout` is the final config and\n//! `LayoutPart` is one part parsed from one config file.\n//!\n//! The convention for `Default` impls is to set the initial values before the parsing occurs.\n//! Then, parsing will update the values with those parsed from the config.\n//!\n//! The `Default` values match those from `default-config.kdl` in almost all cases, with a notable\n//! exception of `binds {}` and some window rules.\n\n#[macro_use]\nextern crate tracing;\n\nuse std::cell::{Cell, RefCell};\nuse std::collections::HashSet;\nuse std::ffi::OsStr;\nuse std::fs::{self, File};\nuse std::io::Write as _;\nuse std::path::{Path, PathBuf};\nuse std::rc::Rc;\n\nuse knuffel::errors::DecodeError;\nuse knuffel::Decode as _;\nuse miette::{miette, Context as _, IntoDiagnostic as _};\n\n#[macro_use]\npub mod macros;\n\npub mod animations;\npub mod appearance;\npub mod binds;\npub mod debug;\npub mod error;\npub mod gestures;\npub mod input;\npub mod layer_rule;\npub mod layout;\npub mod misc;\npub mod output;\npub mod recent_windows;\npub mod utils;\npub mod window_rule;\npub mod workspace;\n\npub use crate::animations::{Animation, Animations};\npub use crate::appearance::*;\npub use crate::binds::*;\npub use crate::debug::Debug;\npub use crate::error::{ConfigIncludeError, ConfigParseResult};\npub use crate::gestures::Gestures;\npub use crate::input::{Input, ModKey, ScrollMethod, TrackLayout, WarpMouseToFocusMode, Xkb};\npub use crate::layer_rule::LayerRule;\npub use crate::layout::*;\npub use crate::misc::*;\npub use crate::output::{Output, OutputName, Outputs, Position, Vrr};\nuse crate::recent_windows::RecentWindowsPart;\npub use crate::recent_windows::{MruDirection, MruFilter, MruPreviews, MruScope, RecentWindows};\npub use crate::utils::FloatOrInt;\nuse crate::utils::{Flag, MergeWith as _};\npub use crate::window_rule::{FloatingPosition, RelativeTo, WindowRule};\npub use crate::workspace::{Workspace, WorkspaceLayoutPart};\n\nconst RECURSION_LIMIT: u8 = 10;\n\n#[derive(Debug, Default, PartialEq)]\npub struct Config {\n    pub input: Input,\n    pub outputs: Outputs,\n    pub spawn_at_startup: Vec<SpawnAtStartup>,\n    pub spawn_sh_at_startup: Vec<SpawnShAtStartup>,\n    pub layout: Layout,\n    pub prefer_no_csd: bool,\n    pub cursor: Cursor,\n    pub screenshot_path: ScreenshotPath,\n    pub clipboard: Clipboard,\n    pub hotkey_overlay: HotkeyOverlay,\n    pub config_notification: ConfigNotification,\n    pub animations: Animations,\n    pub gestures: Gestures,\n    pub overview: Overview,\n    pub environment: Environment,\n    pub xwayland_satellite: XwaylandSatellite,\n    pub window_rules: Vec<WindowRule>,\n    pub layer_rules: Vec<LayerRule>,\n    pub binds: Binds,\n    pub switch_events: SwitchBinds,\n    pub debug: Debug,\n    pub workspaces: Vec<Workspace>,\n    pub recent_windows: RecentWindows,\n}\n\n#[derive(Debug, Clone)]\npub enum ConfigPath {\n    /// Explicitly set config path.\n    ///\n    /// Load the config only from this path, never create it.\n    Explicit(PathBuf),\n\n    /// Default config path.\n    ///\n    /// Prioritize the user path, fallback to the system path, fallback to creating the user path\n    /// at compositor startup.\n    Regular {\n        /// User config path, usually `$XDG_CONFIG_HOME/niri/config.kdl`.\n        user_path: PathBuf,\n        /// System config path, usually `/etc/niri/config.kdl`.\n        system_path: PathBuf,\n    },\n}\n\n// Newtypes for putting information into the knuffel context.\nstruct BasePath(PathBuf);\nstruct RootBase(PathBuf);\nstruct Recursion(u8);\n#[derive(Default)]\nstruct Includes(Vec<PathBuf>);\n#[derive(Default)]\nstruct IncludeErrors(Vec<knuffel::Error>);\n// Used for recursive include detection.\n//\n// We don't *need* it because we have a recursion limit, but it makes for nicer error messages.\nstruct IncludeStack(HashSet<PathBuf>);\nstruct SawMruBinds(Rc<Cell<bool>>);\n\n// Rather than listing all fields and deriving knuffel::Decode, we implement\n// knuffel::DecodeChildren by hand, since we need custom logic for every field anyway: we want to\n// merge the values into the config from the context as we go to support the positionality of\n// includes. The reason we need this type at all is because knuffel's only entry point that allows\n// setting default values on a context is `parse_with_context()` that needs a type to parse.\npub struct ConfigPart;\n\nimpl<S> knuffel::DecodeChildren<S> for ConfigPart\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_children(\n        nodes: &[knuffel::ast::SpannedNode<S>],\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        let _span = tracy_client::span!(\"decode config file\");\n\n        let config = ctx.get::<Rc<RefCell<Config>>>().unwrap().clone();\n        let includes = ctx.get::<Rc<RefCell<Includes>>>().unwrap().clone();\n        let include_errors = ctx.get::<Rc<RefCell<IncludeErrors>>>().unwrap().clone();\n        let recursion = ctx.get::<Recursion>().unwrap().0;\n        let saw_mru_binds = ctx.get::<SawMruBinds>().unwrap().0.clone();\n\n        let mut seen = HashSet::new();\n\n        for node in nodes {\n            let name = &**node.node_name;\n\n            // Within one config file, splitting sections into multiple parts is not allowed to\n            // reduce confusion. The exceptions here aren't multipart; they all add new values.\n            if !matches!(\n                name,\n                \"output\"\n                    | \"spawn-at-startup\"\n                    | \"spawn-sh-at-startup\"\n                    | \"window-rule\"\n                    | \"layer-rule\"\n                    | \"workspace\"\n                    | \"include\"\n            ) && !seen.insert(name)\n            {\n                ctx.emit_error(DecodeError::unexpected(\n                    &node.node_name,\n                    \"node\",\n                    format!(\"duplicate node `{name}`, single node expected\"),\n                ));\n                continue;\n            }\n\n            macro_rules! m_merge {\n                ($field:ident) => {{\n                    let part = knuffel::Decode::decode_node(node, ctx)?;\n                    config.borrow_mut().$field.merge_with(&part);\n                }};\n            }\n\n            macro_rules! m_push {\n                ($field:ident) => {{\n                    let part = knuffel::Decode::decode_node(node, ctx)?;\n                    config.borrow_mut().$field.push(part);\n                }};\n            }\n\n            match name {\n                \"input\" => m_merge!(input),\n                \"cursor\" => m_merge!(cursor),\n                \"clipboard\" => m_merge!(clipboard),\n                \"hotkey-overlay\" => m_merge!(hotkey_overlay),\n                \"config-notification\" => m_merge!(config_notification),\n                \"animations\" => m_merge!(animations),\n                \"gestures\" => m_merge!(gestures),\n                \"overview\" => m_merge!(overview),\n                \"xwayland-satellite\" => m_merge!(xwayland_satellite),\n                \"switch-events\" => m_merge!(switch_events),\n                \"debug\" => m_merge!(debug),\n\n                // Multipart sections.\n                \"output\" => {\n                    let part = Output::decode_node(node, ctx)?;\n                    config.borrow_mut().outputs.0.push(part);\n                }\n                \"spawn-at-startup\" => m_push!(spawn_at_startup),\n                \"spawn-sh-at-startup\" => m_push!(spawn_sh_at_startup),\n                \"window-rule\" => m_push!(window_rules),\n                \"layer-rule\" => m_push!(layer_rules),\n                \"workspace\" => m_push!(workspaces),\n\n                // Single-part sections.\n                \"binds\" => {\n                    let part = Binds::decode_node(node, ctx)?;\n\n                    // We replace conflicting binds, rather than error, to support the use-case\n                    // where you import some preconfigured-dots.kdl, then override some binds with\n                    // your own.\n                    let mut config = config.borrow_mut();\n                    let binds = &mut config.binds.0;\n                    // Remove existing binds matching any new bind.\n                    binds.retain(|bind| !part.0.iter().any(|new| new.key == bind.key));\n                    // Add all new binds.\n                    binds.extend(part.0);\n                }\n                \"environment\" => {\n                    let part = Environment::decode_node(node, ctx)?;\n                    config.borrow_mut().environment.0.extend(part.0);\n                }\n\n                \"prefer-no-csd\" => {\n                    config.borrow_mut().prefer_no_csd = Flag::decode_node(node, ctx)?.0\n                }\n\n                \"screenshot-path\" => {\n                    let part = knuffel::Decode::decode_node(node, ctx)?;\n                    config.borrow_mut().screenshot_path = part;\n                }\n\n                \"layout\" => {\n                    let mut part = LayoutPart::decode_node(node, ctx)?;\n\n                    // Preserve the behavior we'd always had for the border section:\n                    // - `layout {}` gives border = off\n                    // - `layout { border {} }` gives border = on\n                    // - `layout { border { off } }` gives border = off\n                    //\n                    // This behavior is inconsistent with the rest of the config where adding an\n                    // empty section generally doesn't change the outcome. Particularly, shadows\n                    // are also disabled by default (like borders), and they always had an `on`\n                    // instead of an `off` for this reason, so that writing `layout { shadow {} }`\n                    // still results in shadow = off, as it should.\n                    //\n                    // Unfortunately, the default config has always had wording that heavily\n                    // implies that `layout { border {} }` enables the borders. This wording is\n                    // sure to be present in a lot of users' configs by now, which we can't change.\n                    //\n                    // Another way to make things consistent would be to default borders to on.\n                    // However, that is annoying because it would mean changing many tests that\n                    // rely on borders being off by default. This would also contradict the\n                    // intended default borders value (off).\n                    //\n                    // So, let's just work around the problem here, preserving the original\n                    // behavior.\n                    if recursion == 0 {\n                        if let Some(border) = part.border.as_mut() {\n                            if !border.on && !border.off {\n                                border.on = true;\n                            }\n                        }\n                    }\n\n                    config.borrow_mut().layout.merge_with(&part);\n                }\n\n                \"recent-windows\" => {\n                    let part = RecentWindowsPart::decode_node(node, ctx)?;\n\n                    let mut config = config.borrow_mut();\n\n                    // When an MRU binds section is encountered for the first time, clear out the\n                    // default MRU binds.\n                    if !saw_mru_binds.get() && part.binds.is_some() {\n                        saw_mru_binds.set(true);\n                        config.recent_windows.binds.clear();\n                    }\n\n                    config.recent_windows.merge_with(&part);\n                }\n\n                \"include\" => {\n                    // Parse the path argument\n                    let mut iter_args = node.arguments.iter();\n                    let path_val = iter_args.next().ok_or_else(|| {\n                        DecodeError::missing(\n                            node,\n                            \"additional argument for include path is required\",\n                        )\n                    })?;\n                    let path: PathBuf = knuffel::traits::DecodeScalar::decode(path_val, ctx)?;\n\n                    // Check for extra arguments\n                    if let Some(val) = iter_args.next() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            &val.literal,\n                            \"argument\",\n                            \"unexpected argument\",\n                        ));\n                    }\n\n                    // Parse the optional property\n                    let mut optional = false;\n                    for (name, val) in &node.properties {\n                        match &***name {\n                            \"optional\" => {\n                                optional = knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                            }\n                            name_str => {\n                                ctx.emit_error(DecodeError::unexpected(\n                                    name,\n                                    \"property\",\n                                    format!(\"unexpected property `{}`\", name_str.escape_default()),\n                                ));\n                            }\n                        }\n                    }\n\n                    // Check for unexpected children\n                    for child in node.children() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            child,\n                            \"node\",\n                            format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n                        ));\n                    }\n\n                    let base = ctx.get::<BasePath>().unwrap();\n                    let path = base.0.join(path);\n\n                    // We use DecodeError::Missing throughout this block because it results in the\n                    // least confusing error messages while still allowing to provide a span.\n\n                    let recursion = ctx.get::<Recursion>().unwrap().0 + 1;\n                    if recursion == RECURSION_LIMIT {\n                        ctx.emit_error(DecodeError::missing(\n                            node,\n                            format!(\n                                \"reached the recursion limit; \\\n                                 includes cannot be {RECURSION_LIMIT} levels deep\"\n                            ),\n                        ));\n                        continue;\n                    }\n\n                    let Some(filename) = path.file_name().and_then(OsStr::to_str) else {\n                        ctx.emit_error(DecodeError::missing(\n                            node,\n                            \"include path doesn't have a valid file name\",\n                        ));\n                        continue;\n                    };\n                    let base = path.parent().map(Path::to_path_buf).unwrap_or_default();\n\n                    // Check for recursive include for a nicer error message.\n                    let mut include_stack = ctx.get::<IncludeStack>().unwrap().0.clone();\n                    if !include_stack.insert(path.to_path_buf()) {\n                        ctx.emit_error(DecodeError::missing(\n                            node,\n                            \"recursive include (file includes itself)\",\n                        ));\n                        continue;\n                    }\n\n                    // Store even if the include fails to read or parse, so it gets watched.\n                    includes.borrow_mut().0.push(path.to_path_buf());\n\n                    match fs::read_to_string(&path) {\n                        Ok(text) => {\n                            // Try to get filename relative to the root base config folder for\n                            // clearer error messages.\n                            let root_base = &ctx.get::<RootBase>().unwrap().0;\n                            // Failing to strip prefix usually means absolute path; show it in full.\n                            let relative_path = path.strip_prefix(root_base).ok().unwrap_or(&path);\n                            let filename = relative_path.to_str().unwrap_or(filename);\n\n                            let part = knuffel::parse_with_context::<\n                                ConfigPart,\n                                knuffel::span::Span,\n                                _,\n                            >(filename, &text, |ctx| {\n                                ctx.set(BasePath(base));\n                                ctx.set(RootBase(root_base.clone()));\n                                ctx.set(Recursion(recursion));\n                                ctx.set(includes.clone());\n                                ctx.set(include_errors.clone());\n                                ctx.set(IncludeStack(include_stack));\n                                ctx.set(SawMruBinds(saw_mru_binds.clone()));\n                                ctx.set(config.clone());\n                            });\n\n                            match part {\n                                Ok(_) => {}\n                                Err(err) => {\n                                    include_errors.borrow_mut().0.push(err);\n\n                                    ctx.emit_error(DecodeError::missing(\n                                        node,\n                                        \"failed to parse included config\",\n                                    ));\n                                }\n                            }\n                        }\n                        Err(err) => {\n                            if optional && err.kind() == std::io::ErrorKind::NotFound {\n                                // Warn about missing optional includes\n                                warn!(\"optional include not found: {path:?}\");\n                            } else {\n                                // Report all other errors normally\n                                ctx.emit_error(DecodeError::missing(\n                                    node,\n                                    format!(\"failed to read included config from {path:?}: {err}\"),\n                                ));\n                            }\n                        }\n                    }\n                }\n\n                name => {\n                    ctx.emit_error(DecodeError::unexpected(\n                        node,\n                        \"node\",\n                        format!(\"unexpected node `{}`\", name.escape_default()),\n                    ));\n                }\n            }\n        }\n\n        Ok(Self)\n    }\n}\n\nimpl Config {\n    pub fn load_default() -> Self {\n        let res = Config::parse(\n            Path::new(\"default-config.kdl\"),\n            include_str!(\"../../resources/default-config.kdl\"),\n        );\n\n        // Includes in the default config can break its parsing at runtime.\n        assert!(\n            res.includes.is_empty(),\n            \"default config must not have includes\",\n        );\n\n        res.config.unwrap()\n    }\n\n    pub fn load(path: &Path) -> ConfigParseResult<Self, miette::Report> {\n        let contents = match fs::read_to_string(path) {\n            Ok(x) => x,\n            Err(err) => {\n                return ConfigParseResult::from_err(\n                    miette!(err).context(format!(\"error reading {path:?}\")),\n                );\n            }\n        };\n\n        Self::parse(path, &contents).map_config_res(|res| {\n            let config = res.context(\"error parsing\")?;\n            debug!(\"loaded config from {path:?}\");\n            Ok(config)\n        })\n    }\n\n    pub fn parse(path: &Path, text: &str) -> ConfigParseResult<Self, ConfigIncludeError> {\n        let base = path.parent().map(Path::to_path_buf).unwrap_or_default();\n        let filename = path\n            .file_name()\n            .and_then(OsStr::to_str)\n            .unwrap_or(\"config.kdl\");\n\n        let config = Rc::new(RefCell::new(Config::default()));\n        let includes = Rc::new(RefCell::new(Includes(Vec::new())));\n        let include_errors = Rc::new(RefCell::new(IncludeErrors(Vec::new())));\n        let include_stack = HashSet::from([path.to_path_buf()]);\n\n        let part = knuffel::parse_with_context::<ConfigPart, knuffel::span::Span, _>(\n            filename,\n            text,\n            |ctx| {\n                ctx.set(BasePath(base.clone()));\n                ctx.set(RootBase(base));\n                ctx.set(Recursion(0));\n                ctx.set(includes.clone());\n                ctx.set(include_errors.clone());\n                ctx.set(IncludeStack(include_stack));\n                ctx.set(SawMruBinds(Rc::new(Cell::new(false))));\n                ctx.set(config.clone());\n            },\n        );\n\n        let includes = includes.take().0;\n        let include_errors = include_errors.take().0;\n        let config = part\n            .map(|_| config.take())\n            .map_err(move |err| ConfigIncludeError {\n                main: err,\n                includes: include_errors,\n            });\n\n        ConfigParseResult { config, includes }\n    }\n\n    pub fn parse_mem(text: &str) -> Result<Self, ConfigIncludeError> {\n        Self::parse(Path::new(\"config.kdl\"), text).config\n    }\n}\n\nimpl ConfigPath {\n    /// Loads the config, returns an error if it doesn't exist.\n    pub fn load(&self) -> ConfigParseResult<Config, miette::Report> {\n        let _span = tracy_client::span!(\"ConfigPath::load\");\n\n        self.load_inner(|user_path, system_path| {\n            Err(miette!(\n                \"no config file found; create one at {user_path:?} or {system_path:?}\",\n            ))\n        })\n        .map_config_res(|res| res.context(\"error loading config\"))\n    }\n\n    /// Loads the config, or creates it if it doesn't exist.\n    ///\n    /// Returns a tuple containing the path that was created, if any, and the loaded config.\n    ///\n    /// If the config was created, but for some reason could not be read afterwards,\n    /// this may return `(Some(_), Err(_))`.\n    pub fn load_or_create(&self) -> (Option<&Path>, ConfigParseResult<Config, miette::Report>) {\n        let _span = tracy_client::span!(\"ConfigPath::load_or_create\");\n\n        let mut created_at = None;\n\n        let result = self\n            .load_inner(|user_path, _| {\n                Self::create(user_path, &mut created_at)\n                    .map(|()| user_path)\n                    .with_context(|| format!(\"error creating config at {user_path:?}\"))\n            })\n            .map_config_res(|res| res.context(\"error loading config\"));\n\n        (created_at, result)\n    }\n\n    fn load_inner<'a>(\n        &'a self,\n        maybe_create: impl FnOnce(&'a Path, &'a Path) -> miette::Result<&'a Path>,\n    ) -> ConfigParseResult<Config, miette::Report> {\n        let path = match self {\n            ConfigPath::Explicit(path) => path.as_path(),\n            ConfigPath::Regular {\n                user_path,\n                system_path,\n            } => {\n                if user_path.exists() {\n                    user_path.as_path()\n                } else if system_path.exists() {\n                    system_path.as_path()\n                } else {\n                    match maybe_create(user_path.as_path(), system_path.as_path()) {\n                        Ok(x) => x,\n                        Err(err) => return ConfigParseResult::from_err(miette!(err)),\n                    }\n                }\n            }\n        };\n        Config::load(path)\n    }\n\n    fn create<'a>(path: &'a Path, created_at: &mut Option<&'a Path>) -> miette::Result<()> {\n        if let Some(default_parent) = path.parent() {\n            fs::create_dir_all(default_parent)\n                .into_diagnostic()\n                .with_context(|| format!(\"error creating config directory {default_parent:?}\"))?;\n        }\n\n        // Create the config and fill it with the default config if it doesn't exist.\n        let mut new_file = match File::options()\n            .read(true)\n            .write(true)\n            .create_new(true)\n            .open(path)\n        {\n            Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => return Ok(()),\n            res => res,\n        }\n        .into_diagnostic()\n        .with_context(|| format!(\"error opening config file at {path:?}\"))?;\n\n        *created_at = Some(path);\n\n        let default = include_bytes!(\"../../resources/default-config.kdl\");\n\n        new_file\n            .write_all(default)\n            .into_diagnostic()\n            .with_context(|| format!(\"error writing default config to {path:?}\"))?;\n\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::{assert_debug_snapshot, assert_snapshot};\n    use pretty_assertions::assert_eq;\n\n    use super::*;\n\n    #[test]\n    fn can_create_default_config() {\n        let _ = Config::load_default();\n    }\n\n    #[test]\n    fn default_repeat_params() {\n        let config = Config::parse_mem(\"\").unwrap();\n        assert_eq!(config.input.keyboard.repeat_delay, 600);\n        assert_eq!(config.input.keyboard.repeat_rate, 25);\n    }\n\n    #[track_caller]\n    fn do_parse(text: &str) -> Config {\n        Config::parse_mem(text)\n            .map_err(miette::Report::new)\n            .unwrap()\n    }\n\n    #[test]\n    fn parse() {\n        let parsed = do_parse(\n            r##\"\n            input {\n                keyboard {\n                    repeat-delay 600\n                    repeat-rate 25\n                    track-layout \"window\"\n                    xkb {\n                        layout \"us,ru\"\n                        options \"grp:win_space_toggle\"\n                    }\n                }\n\n                touchpad {\n                    tap\n                    dwt\n                    dwtp\n                    drag true\n                    click-method \"clickfinger\"\n                    accel-speed 0.2\n                    accel-profile \"flat\"\n                    scroll-method \"two-finger\"\n                    scroll-button 272\n                    scroll-button-lock\n                    tap-button-map \"left-middle-right\"\n                    disabled-on-external-mouse\n                    scroll-factor 0.9\n                }\n\n                mouse {\n                    natural-scroll\n                    accel-speed 0.4\n                    accel-profile \"flat\"\n                    scroll-method \"no-scroll\"\n                    scroll-button 273\n                    middle-emulation\n                    scroll-factor 0.2\n                }\n\n                trackpoint {\n                    off\n                    natural-scroll\n                    accel-speed 0.0\n                    accel-profile \"flat\"\n                    scroll-method \"on-button-down\"\n                    scroll-button 274\n                }\n\n                trackball {\n                    off\n                    natural-scroll\n                    accel-speed 0.0\n                    accel-profile \"flat\"\n                    scroll-method \"edge\"\n                    scroll-button 275\n                    scroll-button-lock\n                    left-handed\n                    middle-emulation\n                }\n\n                tablet {\n                    map-to-output \"eDP-1\"\n                    calibration-matrix 1.0 2.0 3.0 \\\n                                       4.0 5.0 6.0\n                }\n\n                touch {\n                    map-to-output \"eDP-1\"\n                }\n\n                disable-power-key-handling\n\n                warp-mouse-to-focus\n                focus-follows-mouse\n                workspace-auto-back-and-forth\n\n                mod-key \"Mod5\"\n                mod-key-nested \"Super\"\n            }\n\n            output \"eDP-1\" {\n                focus-at-startup\n                scale 2\n                transform \"flipped-90\"\n                position x=10 y=20\n                mode \"1920x1080@144\"\n                variable-refresh-rate on-demand=true\n                background-color \"rgba(25, 25, 102, 1.0)\"\n                hot-corners {\n                    off\n                    top-left\n                    top-right\n                    bottom-left\n                    bottom-right\n                }\n            }\n\n            output \"eDP-2\" {\n                mode custom=true \"1920x1080@144\"\n            }\n\n            output \"eDP-3\" {\n                modeline 173.00  1920 2048 2248 2576  1080 1083 1088 1120 \"-hsync\" \"+vsync\"\n            }\n\n            layout {\n                focus-ring {\n                    width 5\n                    active-color 0 100 200 255\n                    inactive-color 255 200 100 0\n                    active-gradient from=\"rgba(10, 20, 30, 1.0)\" to=\"#0080ffff\" relative-to=\"workspace-view\"\n                }\n\n                border {\n                    width 3\n                    inactive-color \"rgba(255, 200, 100, 0.0)\"\n                }\n\n                shadow {\n                    offset x=10 y=-20\n                }\n\n                tab-indicator {\n                    width 10\n                    position \"top\"\n                }\n\n                preset-column-widths {\n                    proportion 0.25\n                    proportion 0.5\n                    fixed 960\n                    fixed 1280\n                }\n\n                preset-window-heights {\n                    proportion 0.25\n                    proportion 0.5\n                    fixed 960\n                    fixed 1280\n                }\n\n                default-column-width { proportion 0.25; }\n\n                gaps 8\n\n                struts {\n                    left 1\n                    right 2\n                    top 3\n                }\n\n                center-focused-column \"on-overflow\"\n\n                default-column-display \"tabbed\"\n\n                insert-hint {\n                    color \"rgb(255, 200, 127)\"\n                    gradient from=\"rgba(10, 20, 30, 1.0)\" to=\"#0080ffff\" relative-to=\"workspace-view\"\n                }\n            }\n\n            spawn-at-startup \"alacritty\" \"-e\" \"fish\"\n            spawn-sh-at-startup \"qs -c ~/source/qs/MyAwesomeShell\"\n\n            prefer-no-csd\n\n            cursor {\n                xcursor-theme \"breeze_cursors\"\n                xcursor-size 16\n                hide-when-typing\n                hide-after-inactive-ms 3000\n            }\n\n            screenshot-path \"~/Screenshots/screenshot.png\"\n\n            clipboard {\n                disable-primary\n            }\n\n            hotkey-overlay {\n                skip-at-startup\n            }\n\n            animations {\n                slowdown 2.0\n\n                workspace-switch {\n                    spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001\n                }\n\n                horizontal-view-movement {\n                    duration-ms 100\n                    curve \"ease-out-expo\"\n                }\n\n                window-open { off; }\n\n                window-close {\n                    curve \"cubic-bezier\" 0.05 0.7 0.1 1  \n                }\n\n                recent-windows-close {\n                    off\n                }\n            }\n\n            gestures {\n                dnd-edge-view-scroll {\n                    trigger-width 10\n                    max-speed 50\n                }\n            }\n\n            environment {\n                QT_QPA_PLATFORM \"wayland\"\n                DISPLAY null\n            }\n\n            window-rule {\n                match app-id=\".*alacritty\"\n                exclude title=\"~\"\n                exclude is-active=true is-focused=false\n\n                open-on-output \"eDP-1\"\n                open-maximized true\n                open-fullscreen false\n                open-floating false\n                open-focused true\n                default-window-height { fixed 500; }\n                default-column-display \"tabbed\"\n                default-floating-position x=100 y=-200 relative-to=\"bottom-left\"\n\n                focus-ring {\n                    off\n                    width 3\n                }\n\n                border {\n                    on\n                    width 8.5\n                }\n\n                tab-indicator {\n                    active-color \"#f00\"\n                }\n            }\n\n            layer-rule {\n                match namespace=\"^notifications$\"\n                block-out-from \"screencast\"\n            }\n\n            binds {\n                Mod+Escape hotkey-overlay-title=\"Inhibit\" { toggle-keyboard-shortcuts-inhibit; }\n                Mod+Shift+Escape allow-inhibiting=true { toggle-keyboard-shortcuts-inhibit; }\n                Mod+T allow-when-locked=true { spawn \"alacritty\"; }\n                Mod+Q hotkey-overlay-title=null { close-window; }\n                Mod+Shift+H { focus-monitor-left; }\n                Mod+Shift+O { focus-monitor \"eDP-1\"; }\n                Mod+Ctrl+Shift+L { move-window-to-monitor-right; }\n                Mod+Ctrl+Alt+O { move-window-to-monitor \"eDP-1\"; }\n                Mod+Ctrl+Alt+P { move-column-to-monitor \"DP-1\"; }\n                Mod+Comma { consume-window-into-column; }\n                Mod+1 { focus-workspace 1; }\n                Mod+Shift+1 { focus-workspace \"workspace-1\"; }\n                Mod+Shift+E allow-inhibiting=false { quit skip-confirmation=true; }\n                Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }\n                Super+Alt+S allow-when-locked=true { spawn-sh \"pkill orca || exec orca\"; }\n            }\n\n            switch-events {\n                tablet-mode-on { spawn \"bash\" \"-c\" \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true\"; }\n                tablet-mode-off { spawn \"bash\" \"-c\" \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false\"; }\n            }\n\n            debug {\n                render-drm-device \"/dev/dri/renderD129\"\n                ignore-drm-device \"/dev/dri/renderD128\"\n                ignore-drm-device \"/dev/dri/renderD130\"\n            }\n\n            workspace \"workspace-1\" {\n                open-on-output \"eDP-1\"\n            }\n            workspace \"workspace-2\"\n            workspace \"workspace-3\"\n\n            recent-windows {\n                off\n\n                highlight {\n                    padding 15\n                    active-color \"#00ff00\"\n                }\n\n                previews {\n                    max-height 960\n                }\n\n                binds {\n                    Alt+Tab { next-window; }\n                    Alt+grave { next-window filter=\"app-id\"; }\n                    Super+Tab { next-window scope=\"output\"; }\n                }\n            }\n            \"##,\n        );\n\n        assert_debug_snapshot!(parsed, @r#\"\n        Config {\n            input: Input {\n                keyboard: Keyboard {\n                    xkb: Xkb {\n                        rules: \"\",\n                        model: \"\",\n                        layout: \"us,ru\",\n                        variant: \"\",\n                        options: Some(\n                            \"grp:win_space_toggle\",\n                        ),\n                        file: None,\n                    },\n                    repeat_delay: 600,\n                    repeat_rate: 25,\n                    track_layout: Window,\n                    numlock: false,\n                },\n                touchpad: Touchpad {\n                    off: false,\n                    tap: true,\n                    dwt: true,\n                    dwtp: true,\n                    drag: Some(\n                        true,\n                    ),\n                    drag_lock: false,\n                    natural_scroll: false,\n                    click_method: Some(\n                        Clickfinger,\n                    ),\n                    accel_speed: FloatOrInt(\n                        0.2,\n                    ),\n                    accel_profile: Some(\n                        Flat,\n                    ),\n                    scroll_method: Some(\n                        TwoFinger,\n                    ),\n                    scroll_button: Some(\n                        272,\n                    ),\n                    scroll_button_lock: true,\n                    tap_button_map: Some(\n                        LeftMiddleRight,\n                    ),\n                    left_handed: false,\n                    disabled_on_external_mouse: true,\n                    middle_emulation: false,\n                    scroll_factor: Some(\n                        ScrollFactor {\n                            base: Some(\n                                FloatOrInt(\n                                    0.9,\n                                ),\n                            ),\n                            horizontal: None,\n                            vertical: None,\n                        },\n                    ),\n                },\n                mouse: Mouse {\n                    off: false,\n                    natural_scroll: true,\n                    accel_speed: FloatOrInt(\n                        0.4,\n                    ),\n                    accel_profile: Some(\n                        Flat,\n                    ),\n                    scroll_method: Some(\n                        NoScroll,\n                    ),\n                    scroll_button: Some(\n                        273,\n                    ),\n                    scroll_button_lock: false,\n                    left_handed: false,\n                    middle_emulation: true,\n                    scroll_factor: Some(\n                        ScrollFactor {\n                            base: Some(\n                                FloatOrInt(\n                                    0.2,\n                                ),\n                            ),\n                            horizontal: None,\n                            vertical: None,\n                        },\n                    ),\n                },\n                trackpoint: Trackpoint {\n                    off: true,\n                    natural_scroll: true,\n                    accel_speed: FloatOrInt(\n                        0.0,\n                    ),\n                    accel_profile: Some(\n                        Flat,\n                    ),\n                    scroll_method: Some(\n                        OnButtonDown,\n                    ),\n                    scroll_button: Some(\n                        274,\n                    ),\n                    scroll_button_lock: false,\n                    left_handed: false,\n                    middle_emulation: false,\n                },\n                trackball: Trackball {\n                    off: true,\n                    natural_scroll: true,\n                    accel_speed: FloatOrInt(\n                        0.0,\n                    ),\n                    accel_profile: Some(\n                        Flat,\n                    ),\n                    scroll_method: Some(\n                        Edge,\n                    ),\n                    scroll_button: Some(\n                        275,\n                    ),\n                    scroll_button_lock: true,\n                    left_handed: true,\n                    middle_emulation: true,\n                },\n                tablet: Tablet {\n                    off: false,\n                    calibration_matrix: Some(\n                        [\n                            1.0,\n                            2.0,\n                            3.0,\n                            4.0,\n                            5.0,\n                            6.0,\n                        ],\n                    ),\n                    map_to_output: Some(\n                        \"eDP-1\",\n                    ),\n                    left_handed: false,\n                },\n                touch: Touch {\n                    off: false,\n                    calibration_matrix: None,\n                    map_to_output: Some(\n                        \"eDP-1\",\n                    ),\n                },\n                disable_power_key_handling: true,\n                warp_mouse_to_focus: Some(\n                    WarpMouseToFocus {\n                        mode: None,\n                    },\n                ),\n                focus_follows_mouse: Some(\n                    FocusFollowsMouse {\n                        max_scroll_amount: None,\n                    },\n                ),\n                workspace_auto_back_and_forth: true,\n                mod_key: Some(\n                    IsoLevel3Shift,\n                ),\n                mod_key_nested: Some(\n                    Super,\n                ),\n            },\n            outputs: Outputs(\n                [\n                    Output {\n                        off: false,\n                        name: \"eDP-1\",\n                        scale: Some(\n                            FloatOrInt(\n                                2.0,\n                            ),\n                        ),\n                        transform: Flipped90,\n                        position: Some(\n                            Position {\n                                x: 10,\n                                y: 20,\n                            },\n                        ),\n                        mode: Some(\n                            Mode {\n                                custom: false,\n                                mode: ConfiguredMode {\n                                    width: 1920,\n                                    height: 1080,\n                                    refresh: Some(\n                                        144.0,\n                                    ),\n                                },\n                            },\n                        ),\n                        modeline: None,\n                        variable_refresh_rate: Some(\n                            Vrr {\n                                on_demand: true,\n                            },\n                        ),\n                        focus_at_startup: true,\n                        background_color: Some(\n                            Color {\n                                r: 0.09803922,\n                                g: 0.09803922,\n                                b: 0.4,\n                                a: 1.0,\n                            },\n                        ),\n                        backdrop_color: None,\n                        hot_corners: Some(\n                            HotCorners {\n                                off: true,\n                                top_left: true,\n                                top_right: true,\n                                bottom_left: true,\n                                bottom_right: true,\n                            },\n                        ),\n                        layout: None,\n                    },\n                    Output {\n                        off: false,\n                        name: \"eDP-2\",\n                        scale: None,\n                        transform: Normal,\n                        position: None,\n                        mode: Some(\n                            Mode {\n                                custom: true,\n                                mode: ConfiguredMode {\n                                    width: 1920,\n                                    height: 1080,\n                                    refresh: Some(\n                                        144.0,\n                                    ),\n                                },\n                            },\n                        ),\n                        modeline: None,\n                        variable_refresh_rate: None,\n                        focus_at_startup: false,\n                        background_color: None,\n                        backdrop_color: None,\n                        hot_corners: None,\n                        layout: None,\n                    },\n                    Output {\n                        off: false,\n                        name: \"eDP-3\",\n                        scale: None,\n                        transform: Normal,\n                        position: None,\n                        mode: None,\n                        modeline: Some(\n                            Modeline {\n                                clock: 173.0,\n                                hdisplay: 1920,\n                                hsync_start: 2048,\n                                hsync_end: 2248,\n                                htotal: 2576,\n                                vdisplay: 1080,\n                                vsync_start: 1083,\n                                vsync_end: 1088,\n                                vtotal: 1120,\n                                hsync_polarity: NHSync,\n                                vsync_polarity: PVSync,\n                            },\n                        ),\n                        variable_refresh_rate: None,\n                        focus_at_startup: false,\n                        background_color: None,\n                        backdrop_color: None,\n                        hot_corners: None,\n                        layout: None,\n                    },\n                ],\n            ),\n            spawn_at_startup: [\n                SpawnAtStartup {\n                    command: [\n                        \"alacritty\",\n                        \"-e\",\n                        \"fish\",\n                    ],\n                },\n            ],\n            spawn_sh_at_startup: [\n                SpawnShAtStartup {\n                    command: \"qs -c ~/source/qs/MyAwesomeShell\",\n                },\n            ],\n            layout: Layout {\n                focus_ring: FocusRing {\n                    off: false,\n                    width: 5.0,\n                    active_color: Color {\n                        r: 0.0,\n                        g: 0.39215687,\n                        b: 0.78431374,\n                        a: 1.0,\n                    },\n                    inactive_color: Color {\n                        r: 1.0,\n                        g: 0.78431374,\n                        b: 0.39215687,\n                        a: 0.0,\n                    },\n                    urgent_color: Color {\n                        r: 0.60784316,\n                        g: 0.0,\n                        b: 0.0,\n                        a: 1.0,\n                    },\n                    active_gradient: Some(\n                        Gradient {\n                            from: Color {\n                                r: 0.039215688,\n                                g: 0.078431375,\n                                b: 0.11764706,\n                                a: 1.0,\n                            },\n                            to: Color {\n                                r: 0.0,\n                                g: 0.5019608,\n                                b: 1.0,\n                                a: 1.0,\n                            },\n                            angle: 180,\n                            relative_to: WorkspaceView,\n                            in_: GradientInterpolation {\n                                color_space: Srgb,\n                                hue_interpolation: Shorter,\n                            },\n                        },\n                    ),\n                    inactive_gradient: None,\n                    urgent_gradient: None,\n                },\n                border: Border {\n                    off: false,\n                    width: 3.0,\n                    active_color: Color {\n                        r: 1.0,\n                        g: 0.78431374,\n                        b: 0.49803922,\n                        a: 1.0,\n                    },\n                    inactive_color: Color {\n                        r: 1.0,\n                        g: 0.78431374,\n                        b: 0.39215687,\n                        a: 0.0,\n                    },\n                    urgent_color: Color {\n                        r: 0.60784316,\n                        g: 0.0,\n                        b: 0.0,\n                        a: 1.0,\n                    },\n                    active_gradient: None,\n                    inactive_gradient: None,\n                    urgent_gradient: None,\n                },\n                shadow: Shadow {\n                    on: false,\n                    offset: ShadowOffset {\n                        x: FloatOrInt(\n                            10.0,\n                        ),\n                        y: FloatOrInt(\n                            -20.0,\n                        ),\n                    },\n                    softness: 30.0,\n                    spread: 5.0,\n                    draw_behind_window: false,\n                    color: Color {\n                        r: 0.0,\n                        g: 0.0,\n                        b: 0.0,\n                        a: 0.46666667,\n                    },\n                    inactive_color: None,\n                },\n                tab_indicator: TabIndicator {\n                    off: false,\n                    hide_when_single_tab: false,\n                    place_within_column: false,\n                    gap: 5.0,\n                    width: 10.0,\n                    length: TabIndicatorLength {\n                        total_proportion: Some(\n                            0.5,\n                        ),\n                    },\n                    position: Top,\n                    gaps_between_tabs: 0.0,\n                    corner_radius: 0.0,\n                    active_color: None,\n                    inactive_color: None,\n                    urgent_color: None,\n                    active_gradient: None,\n                    inactive_gradient: None,\n                    urgent_gradient: None,\n                },\n                insert_hint: InsertHint {\n                    off: false,\n                    color: Color {\n                        r: 1.0,\n                        g: 0.78431374,\n                        b: 0.49803922,\n                        a: 1.0,\n                    },\n                    gradient: Some(\n                        Gradient {\n                            from: Color {\n                                r: 0.039215688,\n                                g: 0.078431375,\n                                b: 0.11764706,\n                                a: 1.0,\n                            },\n                            to: Color {\n                                r: 0.0,\n                                g: 0.5019608,\n                                b: 1.0,\n                                a: 1.0,\n                            },\n                            angle: 180,\n                            relative_to: WorkspaceView,\n                            in_: GradientInterpolation {\n                                color_space: Srgb,\n                                hue_interpolation: Shorter,\n                            },\n                        },\n                    ),\n                },\n                preset_column_widths: [\n                    Proportion(\n                        0.25,\n                    ),\n                    Proportion(\n                        0.5,\n                    ),\n                    Fixed(\n                        960,\n                    ),\n                    Fixed(\n                        1280,\n                    ),\n                ],\n                default_column_width: Some(\n                    Proportion(\n                        0.25,\n                    ),\n                ),\n                preset_window_heights: [\n                    Proportion(\n                        0.25,\n                    ),\n                    Proportion(\n                        0.5,\n                    ),\n                    Fixed(\n                        960,\n                    ),\n                    Fixed(\n                        1280,\n                    ),\n                ],\n                center_focused_column: OnOverflow,\n                always_center_single_column: false,\n                empty_workspace_above_first: false,\n                default_column_display: Tabbed,\n                gaps: 8.0,\n                struts: Struts {\n                    left: FloatOrInt(\n                        1.0,\n                    ),\n                    right: FloatOrInt(\n                        2.0,\n                    ),\n                    top: FloatOrInt(\n                        3.0,\n                    ),\n                    bottom: FloatOrInt(\n                        0.0,\n                    ),\n                },\n                background_color: Color {\n                    r: 0.25,\n                    g: 0.25,\n                    b: 0.25,\n                    a: 1.0,\n                },\n            },\n            prefer_no_csd: true,\n            cursor: Cursor {\n                xcursor_theme: \"breeze_cursors\",\n                xcursor_size: 16,\n                hide_when_typing: true,\n                hide_after_inactive_ms: Some(\n                    3000,\n                ),\n            },\n            screenshot_path: ScreenshotPath(\n                Some(\n                    \"~/Screenshots/screenshot.png\",\n                ),\n            ),\n            clipboard: Clipboard {\n                disable_primary: true,\n            },\n            hotkey_overlay: HotkeyOverlay {\n                skip_at_startup: true,\n                hide_not_bound: false,\n            },\n            config_notification: ConfigNotification {\n                disable_failed: false,\n            },\n            animations: Animations {\n                off: false,\n                slowdown: 2.0,\n                workspace_switch: WorkspaceSwitchAnim(\n                    Animation {\n                        off: false,\n                        kind: Spring(\n                            SpringParams {\n                                damping_ratio: 1.0,\n                                stiffness: 1000,\n                                epsilon: 0.0001,\n                            },\n                        ),\n                    },\n                ),\n                window_open: WindowOpenAnim {\n                    anim: Animation {\n                        off: true,\n                        kind: Easing(\n                            EasingParams {\n                                duration_ms: 150,\n                                curve: EaseOutExpo,\n                            },\n                        ),\n                    },\n                    custom_shader: None,\n                },\n                window_close: WindowCloseAnim {\n                    anim: Animation {\n                        off: false,\n                        kind: Easing(\n                            EasingParams {\n                                duration_ms: 150,\n                                curve: CubicBezier(\n                                    0.05,\n                                    0.7,\n                                    0.1,\n                                    1.0,\n                                ),\n                            },\n                        ),\n                    },\n                    custom_shader: None,\n                },\n                horizontal_view_movement: HorizontalViewMovementAnim(\n                    Animation {\n                        off: false,\n                        kind: Easing(\n                            EasingParams {\n                                duration_ms: 100,\n                                curve: EaseOutExpo,\n                            },\n                        ),\n                    },\n                ),\n                window_movement: WindowMovementAnim(\n                    Animation {\n                        off: false,\n                        kind: Spring(\n                            SpringParams {\n                                damping_ratio: 1.0,\n                                stiffness: 800,\n                                epsilon: 0.0001,\n                            },\n                        ),\n                    },\n                ),\n                window_resize: WindowResizeAnim {\n                    anim: Animation {\n                        off: false,\n                        kind: Spring(\n                            SpringParams {\n                                damping_ratio: 1.0,\n                                stiffness: 800,\n                                epsilon: 0.0001,\n                            },\n                        ),\n                    },\n                    custom_shader: None,\n                },\n                config_notification_open_close: ConfigNotificationOpenCloseAnim(\n                    Animation {\n                        off: false,\n                        kind: Spring(\n                            SpringParams {\n                                damping_ratio: 0.6,\n                                stiffness: 1000,\n                                epsilon: 0.001,\n                            },\n                        ),\n                    },\n                ),\n                exit_confirmation_open_close: ExitConfirmationOpenCloseAnim(\n                    Animation {\n                        off: false,\n                        kind: Spring(\n                            SpringParams {\n                                damping_ratio: 0.6,\n                                stiffness: 500,\n                                epsilon: 0.01,\n                            },\n                        ),\n                    },\n                ),\n                screenshot_ui_open: ScreenshotUiOpenAnim(\n                    Animation {\n                        off: false,\n                        kind: Easing(\n                            EasingParams {\n                                duration_ms: 200,\n                                curve: EaseOutQuad,\n                            },\n                        ),\n                    },\n                ),\n                overview_open_close: OverviewOpenCloseAnim(\n                    Animation {\n                        off: false,\n                        kind: Spring(\n                            SpringParams {\n                                damping_ratio: 1.0,\n                                stiffness: 800,\n                                epsilon: 0.0001,\n                            },\n                        ),\n                    },\n                ),\n                recent_windows_close: RecentWindowsCloseAnim(\n                    Animation {\n                        off: true,\n                        kind: Spring(\n                            SpringParams {\n                                damping_ratio: 1.0,\n                                stiffness: 800,\n                                epsilon: 0.001,\n                            },\n                        ),\n                    },\n                ),\n            },\n            gestures: Gestures {\n                dnd_edge_view_scroll: DndEdgeViewScroll {\n                    trigger_width: 10.0,\n                    delay_ms: 100,\n                    max_speed: 50.0,\n                },\n                dnd_edge_workspace_switch: DndEdgeWorkspaceSwitch {\n                    trigger_height: 50.0,\n                    delay_ms: 100,\n                    max_speed: 1500.0,\n                },\n                hot_corners: HotCorners {\n                    off: false,\n                    top_left: false,\n                    top_right: false,\n                    bottom_left: false,\n                    bottom_right: false,\n                },\n            },\n            overview: Overview {\n                zoom: 0.5,\n                backdrop_color: Color {\n                    r: 0.15,\n                    g: 0.15,\n                    b: 0.15,\n                    a: 1.0,\n                },\n                workspace_shadow: WorkspaceShadow {\n                    off: false,\n                    offset: ShadowOffset {\n                        x: FloatOrInt(\n                            0.0,\n                        ),\n                        y: FloatOrInt(\n                            10.0,\n                        ),\n                    },\n                    softness: 40.0,\n                    spread: 10.0,\n                    color: Color {\n                        r: 0.0,\n                        g: 0.0,\n                        b: 0.0,\n                        a: 0.3137255,\n                    },\n                },\n            },\n            environment: Environment(\n                [\n                    EnvironmentVariable {\n                        name: \"QT_QPA_PLATFORM\",\n                        value: Some(\n                            \"wayland\",\n                        ),\n                    },\n                    EnvironmentVariable {\n                        name: \"DISPLAY\",\n                        value: None,\n                    },\n                ],\n            ),\n            xwayland_satellite: XwaylandSatellite {\n                off: false,\n                path: \"xwayland-satellite\",\n            },\n            window_rules: [\n                WindowRule {\n                    matches: [\n                        Match {\n                            app_id: Some(\n                                RegexEq(\n                                    Regex(\n                                        \".*alacritty\",\n                                    ),\n                                ),\n                            ),\n                            title: None,\n                            is_active: None,\n                            is_focused: None,\n                            is_active_in_column: None,\n                            is_floating: None,\n                            is_window_cast_target: None,\n                            is_urgent: None,\n                            at_startup: None,\n                        },\n                    ],\n                    excludes: [\n                        Match {\n                            app_id: None,\n                            title: Some(\n                                RegexEq(\n                                    Regex(\n                                        \"~\",\n                                    ),\n                                ),\n                            ),\n                            is_active: None,\n                            is_focused: None,\n                            is_active_in_column: None,\n                            is_floating: None,\n                            is_window_cast_target: None,\n                            is_urgent: None,\n                            at_startup: None,\n                        },\n                        Match {\n                            app_id: None,\n                            title: None,\n                            is_active: Some(\n                                true,\n                            ),\n                            is_focused: Some(\n                                false,\n                            ),\n                            is_active_in_column: None,\n                            is_floating: None,\n                            is_window_cast_target: None,\n                            is_urgent: None,\n                            at_startup: None,\n                        },\n                    ],\n                    default_column_width: None,\n                    default_window_height: Some(\n                        DefaultPresetSize(\n                            Some(\n                                Fixed(\n                                    500,\n                                ),\n                            ),\n                        ),\n                    ),\n                    open_on_output: Some(\n                        \"eDP-1\",\n                    ),\n                    open_on_workspace: None,\n                    open_maximized: Some(\n                        true,\n                    ),\n                    open_maximized_to_edges: None,\n                    open_fullscreen: Some(\n                        false,\n                    ),\n                    open_floating: Some(\n                        false,\n                    ),\n                    open_focused: Some(\n                        true,\n                    ),\n                    min_width: None,\n                    min_height: None,\n                    max_width: None,\n                    max_height: None,\n                    focus_ring: BorderRule {\n                        off: true,\n                        on: false,\n                        width: Some(\n                            FloatOrInt(\n                                3.0,\n                            ),\n                        ),\n                        active_color: None,\n                        inactive_color: None,\n                        urgent_color: None,\n                        active_gradient: None,\n                        inactive_gradient: None,\n                        urgent_gradient: None,\n                    },\n                    border: BorderRule {\n                        off: false,\n                        on: true,\n                        width: Some(\n                            FloatOrInt(\n                                8.5,\n                            ),\n                        ),\n                        active_color: None,\n                        inactive_color: None,\n                        urgent_color: None,\n                        active_gradient: None,\n                        inactive_gradient: None,\n                        urgent_gradient: None,\n                    },\n                    shadow: ShadowRule {\n                        off: false,\n                        on: false,\n                        offset: None,\n                        softness: None,\n                        spread: None,\n                        draw_behind_window: None,\n                        color: None,\n                        inactive_color: None,\n                    },\n                    tab_indicator: TabIndicatorRule {\n                        active_color: Some(\n                            Color {\n                                r: 1.0,\n                                g: 0.0,\n                                b: 0.0,\n                                a: 1.0,\n                            },\n                        ),\n                        inactive_color: None,\n                        urgent_color: None,\n                        active_gradient: None,\n                        inactive_gradient: None,\n                        urgent_gradient: None,\n                    },\n                    draw_border_with_background: None,\n                    opacity: None,\n                    geometry_corner_radius: None,\n                    clip_to_geometry: None,\n                    baba_is_float: None,\n                    block_out_from: None,\n                    variable_refresh_rate: None,\n                    default_column_display: Some(\n                        Tabbed,\n                    ),\n                    default_floating_position: Some(\n                        FloatingPosition {\n                            x: FloatOrInt(\n                                100.0,\n                            ),\n                            y: FloatOrInt(\n                                -200.0,\n                            ),\n                            relative_to: BottomLeft,\n                        },\n                    ),\n                    scroll_factor: None,\n                    tiled_state: None,\n                },\n            ],\n            layer_rules: [\n                LayerRule {\n                    matches: [\n                        Match {\n                            namespace: Some(\n                                RegexEq(\n                                    Regex(\n                                        \"^notifications$\",\n                                    ),\n                                ),\n                            ),\n                            at_startup: None,\n                        },\n                    ],\n                    excludes: [],\n                    opacity: None,\n                    block_out_from: Some(\n                        Screencast,\n                    ),\n                    shadow: ShadowRule {\n                        off: false,\n                        on: false,\n                        offset: None,\n                        softness: None,\n                        spread: None,\n                        draw_behind_window: None,\n                        color: None,\n                        inactive_color: None,\n                    },\n                    geometry_corner_radius: None,\n                    place_within_backdrop: None,\n                    baba_is_float: None,\n                },\n            ],\n            binds: Binds(\n                [\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_Escape,\n                            ),\n                            modifiers: Modifiers(\n                                COMPOSITOR,\n                            ),\n                        },\n                        action: ToggleKeyboardShortcutsInhibit,\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: false,\n                        hotkey_overlay_title: Some(\n                            Some(\n                                \"Inhibit\",\n                            ),\n                        ),\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_Escape,\n                            ),\n                            modifiers: Modifiers(\n                                SHIFT | COMPOSITOR,\n                            ),\n                        },\n                        action: ToggleKeyboardShortcutsInhibit,\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: false,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_t,\n                            ),\n                            modifiers: Modifiers(\n                                COMPOSITOR,\n                            ),\n                        },\n                        action: Spawn(\n                            [\n                                \"alacritty\",\n                            ],\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: true,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_q,\n                            ),\n                            modifiers: Modifiers(\n                                COMPOSITOR,\n                            ),\n                        },\n                        action: CloseWindow,\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: Some(\n                            None,\n                        ),\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_h,\n                            ),\n                            modifiers: Modifiers(\n                                SHIFT | COMPOSITOR,\n                            ),\n                        },\n                        action: FocusMonitorLeft,\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_o,\n                            ),\n                            modifiers: Modifiers(\n                                SHIFT | COMPOSITOR,\n                            ),\n                        },\n                        action: FocusMonitor(\n                            \"eDP-1\",\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_l,\n                            ),\n                            modifiers: Modifiers(\n                                CTRL | SHIFT | COMPOSITOR,\n                            ),\n                        },\n                        action: MoveWindowToMonitorRight,\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_o,\n                            ),\n                            modifiers: Modifiers(\n                                CTRL | ALT | COMPOSITOR,\n                            ),\n                        },\n                        action: MoveWindowToMonitor(\n                            \"eDP-1\",\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_p,\n                            ),\n                            modifiers: Modifiers(\n                                CTRL | ALT | COMPOSITOR,\n                            ),\n                        },\n                        action: MoveColumnToMonitor(\n                            \"DP-1\",\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_comma,\n                            ),\n                            modifiers: Modifiers(\n                                COMPOSITOR,\n                            ),\n                        },\n                        action: ConsumeWindowIntoColumn,\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_1,\n                            ),\n                            modifiers: Modifiers(\n                                COMPOSITOR,\n                            ),\n                        },\n                        action: FocusWorkspace(\n                            Index(\n                                1,\n                            ),\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_1,\n                            ),\n                            modifiers: Modifiers(\n                                SHIFT | COMPOSITOR,\n                            ),\n                        },\n                        action: FocusWorkspace(\n                            Name(\n                                \"workspace-1\",\n                            ),\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_e,\n                            ),\n                            modifiers: Modifiers(\n                                SHIFT | COMPOSITOR,\n                            ),\n                        },\n                        action: Quit(\n                            true,\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: false,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: WheelScrollDown,\n                            modifiers: Modifiers(\n                                COMPOSITOR,\n                            ),\n                        },\n                        action: FocusWorkspaceDown,\n                        repeat: true,\n                        cooldown: Some(\n                            150ms,\n                        ),\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_s,\n                            ),\n                            modifiers: Modifiers(\n                                ALT | SUPER,\n                            ),\n                        },\n                        action: SpawnSh(\n                            \"pkill orca || exec orca\",\n                        ),\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: true,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                ],\n            ),\n            switch_events: SwitchBinds {\n                lid_open: None,\n                lid_close: None,\n                tablet_mode_on: Some(\n                    SwitchAction {\n                        spawn: [\n                            \"bash\",\n                            \"-c\",\n                            \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true\",\n                        ],\n                    },\n                ),\n                tablet_mode_off: Some(\n                    SwitchAction {\n                        spawn: [\n                            \"bash\",\n                            \"-c\",\n                            \"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false\",\n                        ],\n                    },\n                ),\n            },\n            debug: Debug {\n                preview_render: None,\n                dbus_interfaces_in_non_session_instances: false,\n                wait_for_frame_completion_before_queueing: false,\n                enable_overlay_planes: false,\n                disable_cursor_plane: false,\n                disable_direct_scanout: false,\n                keep_max_bpc_unchanged: false,\n                restrict_primary_scanout_to_matching_format: false,\n                force_disable_connectors_on_resume: false,\n                render_drm_device: Some(\n                    \"/dev/dri/renderD129\",\n                ),\n                ignored_drm_devices: [\n                    \"/dev/dri/renderD128\",\n                    \"/dev/dri/renderD130\",\n                ],\n                force_pipewire_invalid_modifier: false,\n                emulate_zero_presentation_time: false,\n                disable_resize_throttling: false,\n                disable_transactions: false,\n                keep_laptop_panel_on_when_lid_is_closed: false,\n                disable_monitor_names: false,\n                strict_new_window_focus_policy: false,\n                honor_xdg_activation_with_invalid_serial: false,\n                deactivate_unfocused_windows: false,\n                skip_cursor_only_updates_during_vrr: false,\n            },\n            workspaces: [\n                Workspace {\n                    name: WorkspaceName(\n                        \"workspace-1\",\n                    ),\n                    open_on_output: Some(\n                        \"eDP-1\",\n                    ),\n                    layout: None,\n                },\n                Workspace {\n                    name: WorkspaceName(\n                        \"workspace-2\",\n                    ),\n                    open_on_output: None,\n                    layout: None,\n                },\n                Workspace {\n                    name: WorkspaceName(\n                        \"workspace-3\",\n                    ),\n                    open_on_output: None,\n                    layout: None,\n                },\n            ],\n            recent_windows: RecentWindows {\n                on: false,\n                debounce_ms: 750,\n                open_delay_ms: 150,\n                highlight: MruHighlight {\n                    active_color: Color {\n                        r: 0.0,\n                        g: 1.0,\n                        b: 0.0,\n                        a: 1.0,\n                    },\n                    urgent_color: Color {\n                        r: 1.0,\n                        g: 0.6,\n                        b: 0.6,\n                        a: 1.0,\n                    },\n                    padding: 15.0,\n                    corner_radius: 0.0,\n                },\n                previews: MruPreviews {\n                    max_height: 960.0,\n                    max_scale: 0.5,\n                },\n                binds: [\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_Tab,\n                            ),\n                            modifiers: Modifiers(\n                                ALT,\n                            ),\n                        },\n                        action: MruAdvance {\n                            direction: Forward,\n                            scope: None,\n                            filter: Some(\n                                All,\n                            ),\n                        },\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_grave,\n                            ),\n                            modifiers: Modifiers(\n                                ALT,\n                            ),\n                        },\n                        action: MruAdvance {\n                            direction: Forward,\n                            scope: None,\n                            filter: Some(\n                                AppId,\n                            ),\n                        },\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                    Bind {\n                        key: Key {\n                            trigger: Keysym(\n                                XK_Tab,\n                            ),\n                            modifiers: Modifiers(\n                                SUPER,\n                            ),\n                        },\n                        action: MruAdvance {\n                            direction: Forward,\n                            scope: Some(\n                                Output,\n                            ),\n                            filter: Some(\n                                All,\n                            ),\n                        },\n                        repeat: true,\n                        cooldown: None,\n                        allow_when_locked: false,\n                        allow_inhibiting: true,\n                        hotkey_overlay_title: None,\n                    },\n                ],\n            },\n        }\n        \"#);\n    }\n\n    fn diff_lines(expected: &str, actual: &str) -> String {\n        let mut output = String::new();\n        let mut in_change = false;\n\n        for change in diff::lines(expected, actual) {\n            match change {\n                diff::Result::Both(_, _) => {\n                    in_change = false;\n                }\n                diff::Result::Left(line) => {\n                    if !output.is_empty() && !in_change {\n                        output.push('\\n');\n                    }\n                    output.push('-');\n                    output.push_str(line);\n                    output.push('\\n');\n                    in_change = true;\n                }\n                diff::Result::Right(line) => {\n                    if !output.is_empty() && !in_change {\n                        output.push('\\n');\n                    }\n                    output.push('+');\n                    output.push_str(line);\n                    output.push('\\n');\n                    in_change = true;\n                }\n            }\n        }\n\n        output\n    }\n\n    #[test]\n    fn diff_empty_to_default() {\n        // We try to write the config defaults in such a way that empty sections (and an empty\n        // config) give the same outcome as the default config bundled with niri. This test\n        // verifies the actual differences between the two.\n        let mut default_config = Config::load_default();\n        let empty_config = Config::parse_mem(\"\").unwrap();\n\n        // Some notable omissions: the default config has some window rules, and an empty config\n        // will not have any binds. Clear them out so they don't spam the diff.\n        default_config.window_rules.clear();\n        default_config.binds.0.clear();\n\n        assert_snapshot!(\n            diff_lines(\n                &format!(\"{empty_config:#?}\"),\n                &format!(\"{default_config:#?}\")\n            ),\n            @r#\"\n        -            numlock: false,\n        +            numlock: true,\n\n        -            tap: false,\n        +            tap: true,\n\n        -            natural_scroll: false,\n        +            natural_scroll: true,\n\n        -    spawn_at_startup: [],\n        +    spawn_at_startup: [\n        +        SpawnAtStartup {\n        +            command: [\n        +                \"waybar\",\n        +            ],\n        +        },\n        +    ],\n\n        -                0.3333333333333333,\n        +                0.33333,\n\n        -                0.6666666666666666,\n        +                0.66667,\n        \"#,\n        );\n    }\n}\n"
  },
  {
    "path": "niri-config/src/macros.rs",
    "content": "macro_rules! merge {\n    (($self:expr, $part:expr), $($field:ident),+ $(,)*) => {\n        $(\n            if let Some(x) = &$part.$field {\n                $self.$field.merge_with(x);\n            }\n        )+\n    };\n}\n\nmacro_rules! merge_clone {\n    (($self:expr, $part:expr), $($field:ident),+ $(,)*) => {\n        $(\n            if let Some(x) = &$part.$field {\n                $self.$field.clone_from(x);\n            }\n        )+\n    };\n}\n\nmacro_rules! merge_clone_opt {\n    (($self:expr, $part:expr), $($field:ident),+ $(,)*) => {\n        $(\n            if $part.$field.is_some() {\n                $self.$field.clone_from(&$part.$field);\n            }\n        )+\n    };\n}\n\nmacro_rules! merge_color_gradient {\n    (($self:expr, $part:expr), $(($color:ident, $gradient:ident)),+ $(,)*) => {\n        $(\n            if let Some(x) = $part.$color {\n                $self.$color = x;\n                $self.$gradient = None;\n            }\n            if let Some(x) = $part.$gradient {\n                $self.$gradient = Some(x);\n            }\n        )+\n    };\n}\n\nmacro_rules! merge_color_gradient_opt {\n    (($self:expr, $part:expr), $(($color:ident, $gradient:ident)),+ $(,)*) => {\n        $(\n            if let Some(x) = $part.$color {\n                $self.$color = Some(x);\n                $self.$gradient = None;\n            }\n            if let Some(x) = $part.$gradient {\n                $self.$gradient = Some(x);\n            }\n        )+\n    };\n}\n\nmacro_rules! merge_on_off {\n    (($self:expr, $part:expr)) => {\n        if $part.off {\n            $self.off = true;\n            $self.on = false;\n        }\n\n        if $part.on {\n            $self.off = false;\n            $self.on = true;\n        }\n    };\n}\n"
  },
  {
    "path": "niri-config/src/misc.rs",
    "content": "use crate::appearance::{Color, WorkspaceShadow, WorkspaceShadowPart, DEFAULT_BACKDROP_COLOR};\nuse crate::utils::{Flag, MergeWith};\nuse crate::FloatOrInt;\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]\npub struct SpawnAtStartup {\n    #[knuffel(arguments)]\n    pub command: Vec<String>,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]\npub struct SpawnShAtStartup {\n    #[knuffel(argument)]\n    pub command: String,\n}\n\n#[derive(Debug, PartialEq)]\npub struct Cursor {\n    pub xcursor_theme: String,\n    pub xcursor_size: u8,\n    pub hide_when_typing: bool,\n    pub hide_after_inactive_ms: Option<u32>,\n}\n\nimpl Default for Cursor {\n    fn default() -> Self {\n        Self {\n            xcursor_theme: String::from(\"default\"),\n            xcursor_size: 24,\n            hide_when_typing: false,\n            hide_after_inactive_ms: None,\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, PartialEq)]\npub struct CursorPart {\n    #[knuffel(child, unwrap(argument))]\n    pub xcursor_theme: Option<String>,\n    #[knuffel(child, unwrap(argument))]\n    pub xcursor_size: Option<u8>,\n    #[knuffel(child)]\n    pub hide_when_typing: Option<Flag>,\n    #[knuffel(child, unwrap(argument))]\n    pub hide_after_inactive_ms: Option<u32>,\n}\n\nimpl MergeWith<CursorPart> for Cursor {\n    fn merge_with(&mut self, part: &CursorPart) {\n        merge_clone!((self, part), xcursor_theme, xcursor_size);\n        merge!((self, part), hide_when_typing);\n        merge_clone_opt!((self, part), hide_after_inactive_ms);\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq)]\npub struct ScreenshotPath(#[knuffel(argument)] pub Option<String>);\n\nimpl Default for ScreenshotPath {\n    fn default() -> Self {\n        Self(Some(String::from(\n            \"~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png\",\n        )))\n    }\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub struct HotkeyOverlay {\n    pub skip_at_startup: bool,\n    pub hide_not_bound: bool,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]\npub struct HotkeyOverlayPart {\n    #[knuffel(child)]\n    pub skip_at_startup: Option<Flag>,\n    #[knuffel(child)]\n    pub hide_not_bound: Option<Flag>,\n}\n\nimpl MergeWith<HotkeyOverlayPart> for HotkeyOverlay {\n    fn merge_with(&mut self, part: &HotkeyOverlayPart) {\n        merge!((self, part), skip_at_startup, hide_not_bound);\n    }\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub struct ConfigNotification {\n    pub disable_failed: bool,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]\npub struct ConfigNotificationPart {\n    #[knuffel(child)]\n    pub disable_failed: Option<Flag>,\n}\n\nimpl MergeWith<ConfigNotificationPart> for ConfigNotification {\n    fn merge_with(&mut self, part: &ConfigNotificationPart) {\n        merge!((self, part), disable_failed);\n    }\n}\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub struct Clipboard {\n    pub disable_primary: bool,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]\npub struct ClipboardPart {\n    #[knuffel(child)]\n    pub disable_primary: Option<Flag>,\n}\n\nimpl MergeWith<ClipboardPart> for Clipboard {\n    fn merge_with(&mut self, part: &ClipboardPart) {\n        merge!((self, part), disable_primary);\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Overview {\n    pub zoom: f64,\n    pub backdrop_color: Color,\n    pub workspace_shadow: WorkspaceShadow,\n}\n\nimpl Default for Overview {\n    fn default() -> Self {\n        Self {\n            zoom: 0.5,\n            backdrop_color: DEFAULT_BACKDROP_COLOR,\n            workspace_shadow: WorkspaceShadow::default(),\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct OverviewPart {\n    #[knuffel(child, unwrap(argument))]\n    pub zoom: Option<FloatOrInt<0, 1>>,\n    #[knuffel(child)]\n    pub backdrop_color: Option<Color>,\n    #[knuffel(child)]\n    pub workspace_shadow: Option<WorkspaceShadowPart>,\n}\n\nimpl MergeWith<OverviewPart> for Overview {\n    fn merge_with(&mut self, part: &OverviewPart) {\n        merge!((self, part), zoom, workspace_shadow);\n        merge_clone!((self, part), backdrop_color);\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq, Eq)]\npub struct Environment(#[knuffel(children)] pub Vec<EnvironmentVariable>);\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]\npub struct EnvironmentVariable {\n    #[knuffel(node_name)]\n    pub name: String,\n    #[knuffel(argument)]\n    pub value: Option<String>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct XwaylandSatellite {\n    pub off: bool,\n    pub path: String,\n}\n\nimpl Default for XwaylandSatellite {\n    fn default() -> Self {\n        Self {\n            off: false,\n            path: String::from(\"xwayland-satellite\"),\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]\npub struct XwaylandSatellitePart {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child, unwrap(argument))]\n    pub path: Option<String>,\n}\n\nimpl MergeWith<XwaylandSatellitePart> for XwaylandSatellite {\n    fn merge_with(&mut self, part: &XwaylandSatellitePart) {\n        self.off |= part.off;\n        if part.on {\n            self.off = false;\n        }\n\n        merge_clone!((self, part), path);\n    }\n}\n"
  },
  {
    "path": "niri-config/src/output.rs",
    "content": "use std::str::FromStr;\n\nuse knuffel::ast::SpannedNode;\nuse knuffel::decode::Context;\nuse knuffel::errors::DecodeError;\nuse knuffel::traits::ErrorSpan;\nuse knuffel::Decode;\nuse niri_ipc::{ConfiguredMode, HSyncPolarity, Transform, VSyncPolarity};\n\nuse crate::gestures::HotCorners;\nuse crate::{Color, FloatOrInt, LayoutPart};\n\n#[derive(Debug, Default, Clone, PartialEq)]\npub struct Outputs(pub Vec<Output>);\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Mode {\n    pub custom: bool,\n    pub mode: ConfiguredMode,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Modeline {\n    /// The rate at which pixels are drawn in MHz.\n    pub clock: f64,\n    /// Horizontal active pixels.\n    pub hdisplay: u16,\n    /// Horizontal sync pulse start position in pixels.\n    pub hsync_start: u16,\n    /// Horizontal sync pulse end position in pixels.\n    pub hsync_end: u16,\n    /// Total horizontal number of pixels before resetting the horizontal drawing position to\n    /// zero.\n    pub htotal: u16,\n\n    /// Vertical active pixels.\n    pub vdisplay: u16,\n    /// Vertical sync pulse start position in pixels.\n    pub vsync_start: u16,\n    /// Vertical sync pulse end position in pixels.\n    pub vsync_end: u16,\n    /// Total vertical number of pixels before resetting the vertical drawing position to zero.\n    pub vtotal: u16,\n    /// Horizontal sync polarity: \"+hsync\" or \"-hsync\".\n    pub hsync_polarity: niri_ipc::HSyncPolarity,\n    /// Vertical sync polarity: \"+vsync\" or \"-vsync\".\n    pub vsync_polarity: niri_ipc::VSyncPolarity,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq)]\npub struct Output {\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(argument)]\n    pub name: String,\n    #[knuffel(child, unwrap(argument))]\n    pub scale: Option<FloatOrInt<0, 10>>,\n    #[knuffel(child, unwrap(argument, str), default = Transform::Normal)]\n    pub transform: Transform,\n    #[knuffel(child)]\n    pub position: Option<Position>,\n    #[knuffel(child)]\n    pub mode: Option<Mode>,\n    #[knuffel(child)]\n    pub modeline: Option<Modeline>,\n    #[knuffel(child)]\n    pub variable_refresh_rate: Option<Vrr>,\n    #[knuffel(child)]\n    pub focus_at_startup: bool,\n    // Deprecated; use layout.background_color.\n    #[knuffel(child)]\n    pub background_color: Option<Color>,\n    #[knuffel(child)]\n    pub backdrop_color: Option<Color>,\n    #[knuffel(child)]\n    pub hot_corners: Option<HotCorners>,\n    #[knuffel(child)]\n    pub layout: Option<LayoutPart>,\n}\n\nimpl Output {\n    pub fn is_vrr_always_on(&self) -> bool {\n        self.variable_refresh_rate == Some(Vrr { on_demand: false })\n    }\n\n    pub fn is_vrr_on_demand(&self) -> bool {\n        self.variable_refresh_rate == Some(Vrr { on_demand: true })\n    }\n\n    pub fn is_vrr_always_off(&self) -> bool {\n        self.variable_refresh_rate.is_none()\n    }\n}\n\nimpl Default for Output {\n    fn default() -> Self {\n        Self {\n            off: false,\n            focus_at_startup: false,\n            name: String::new(),\n            scale: None,\n            transform: Transform::Normal,\n            position: None,\n            mode: None,\n            modeline: None,\n            variable_refresh_rate: None,\n            background_color: None,\n            backdrop_color: None,\n            hot_corners: None,\n            layout: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct OutputName {\n    pub connector: String,\n    pub make: Option<String>,\n    pub model: Option<String>,\n    pub serial: Option<String>,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq, Eq)]\npub struct Position {\n    #[knuffel(property)]\n    pub x: i32,\n    #[knuffel(property)]\n    pub y: i32,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq, Default)]\npub struct Vrr {\n    #[knuffel(property, default = false)]\n    pub on_demand: bool,\n}\n\nimpl FromIterator<Output> for Outputs {\n    fn from_iter<T: IntoIterator<Item = Output>>(iter: T) -> Self {\n        Self(Vec::from_iter(iter))\n    }\n}\n\nimpl Outputs {\n    pub fn find(&self, name: &OutputName) -> Option<&Output> {\n        self.0.iter().find(|o| name.matches(&o.name))\n    }\n\n    pub fn find_mut(&mut self, name: &OutputName) -> Option<&mut Output> {\n        self.0.iter_mut().find(|o| name.matches(&o.name))\n    }\n}\n\nimpl OutputName {\n    pub fn from_ipc_output(output: &niri_ipc::Output) -> Self {\n        Self {\n            connector: output.name.clone(),\n            make: (output.make != \"Unknown\").then(|| output.make.clone()),\n            model: (output.model != \"Unknown\").then(|| output.model.clone()),\n            serial: output.serial.clone(),\n        }\n    }\n\n    /// Returns an output description matching what Smithay's `Output::new()` does.\n    pub fn format_description(&self) -> String {\n        format!(\n            \"{} - {} - {}\",\n            self.make.as_deref().unwrap_or(\"Unknown\"),\n            self.model.as_deref().unwrap_or(\"Unknown\"),\n            self.connector,\n        )\n    }\n\n    /// Returns an output name that will match by make/model/serial or, if they are missing, by\n    /// connector.\n    pub fn format_make_model_serial_or_connector(&self) -> String {\n        if self.make.is_none() && self.model.is_none() && self.serial.is_none() {\n            self.connector.to_string()\n        } else {\n            self.format_make_model_serial()\n        }\n    }\n\n    pub fn format_make_model_serial(&self) -> String {\n        let make = self.make.as_deref().unwrap_or(\"Unknown\");\n        let model = self.model.as_deref().unwrap_or(\"Unknown\");\n        let serial = self.serial.as_deref().unwrap_or(\"Unknown\");\n        format!(\"{make} {model} {serial}\")\n    }\n\n    pub fn matches(&self, target: &str) -> bool {\n        // Match by connector.\n        if target.eq_ignore_ascii_case(&self.connector) {\n            return true;\n        }\n\n        // If no other fields are available, don't try to match by them.\n        //\n        // This is used by niri msg output.\n        if self.make.is_none() && self.model.is_none() && self.serial.is_none() {\n            return false;\n        }\n\n        // Match by \"make model serial\" with Unknown if something is missing.\n        let make = self.make.as_deref().unwrap_or(\"Unknown\");\n        let model = self.model.as_deref().unwrap_or(\"Unknown\");\n        let serial = self.serial.as_deref().unwrap_or(\"Unknown\");\n\n        let Some(target_make) = target.get(..make.len()) else {\n            return false;\n        };\n        let rest = &target[make.len()..];\n        if !target_make.eq_ignore_ascii_case(make) {\n            return false;\n        }\n        if !rest.starts_with(' ') {\n            return false;\n        }\n        let rest = &rest[1..];\n\n        let Some(target_model) = rest.get(..model.len()) else {\n            return false;\n        };\n        let rest = &rest[model.len()..];\n        if !target_model.eq_ignore_ascii_case(model) {\n            return false;\n        }\n        if !rest.starts_with(' ') {\n            return false;\n        }\n\n        let rest = &rest[1..];\n        if !rest.eq_ignore_ascii_case(serial) {\n            return false;\n        }\n\n        true\n    }\n\n    // Similar in spirit to Ord, but I don't want to derive Eq to avoid mistakes (you should use\n    // `Self::match`, not Eq).\n    pub fn compare(&self, other: &Self) -> std::cmp::Ordering {\n        let self_missing_mms = self.make.is_none() && self.model.is_none() && self.serial.is_none();\n        let other_missing_mms =\n            other.make.is_none() && other.model.is_none() && other.serial.is_none();\n\n        match (self_missing_mms, other_missing_mms) {\n            (true, true) => self.connector.cmp(&other.connector),\n            (true, false) => std::cmp::Ordering::Greater,\n            (false, true) => std::cmp::Ordering::Less,\n            (false, false) => self\n                .make\n                .cmp(&other.make)\n                .then_with(|| self.model.cmp(&other.model))\n                .then_with(|| self.serial.cmp(&other.serial))\n                .then_with(|| self.connector.cmp(&other.connector)),\n        }\n    }\n}\n\nimpl<S: ErrorSpan> knuffel::Decode<S> for Mode {\n    fn decode_node(node: &SpannedNode<S>, ctx: &mut Context<S>) -> Result<Self, DecodeError<S>> {\n        if let Some(type_name) = &node.type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n\n        for child in node.children() {\n            ctx.emit_error(DecodeError::unexpected(\n                child,\n                \"node\",\n                format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n            ));\n        }\n\n        let mut custom: Option<bool> = None;\n        for (name, val) in &node.properties {\n            match &***name {\n                \"custom\" => {\n                    if custom.is_some() {\n                        ctx.emit_error(DecodeError::unexpected(\n                            name,\n                            \"property\",\n                            \"unexpected duplicate property `custom`\",\n                        ))\n                    }\n                    custom = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?)\n                }\n                name_str => ctx.emit_error(DecodeError::unexpected(\n                    node,\n                    \"property\",\n                    format!(\"unexpected property `{}`\", name_str.escape_default()),\n                )),\n            }\n        }\n        let custom = custom.unwrap_or(false);\n\n        let mut arguments = node.arguments.iter();\n        let mode = if let Some(mode_str) = arguments.next() {\n            let temp_mode: String = knuffel::traits::DecodeScalar::decode(mode_str, ctx)?;\n\n            let res = ConfiguredMode::from_str(temp_mode.as_str()).and_then(|mode| {\n                if custom {\n                    if mode.refresh.is_none() {\n                        return Err(\"no refresh rate found; required for custom mode\");\n                    } else if let Some(refresh) = mode.refresh {\n                        if refresh <= 0. {\n                            return Err(\"custom mode refresh rate must be > 0\");\n                        }\n                    }\n                }\n                Ok(mode)\n            });\n            res.map_err(|err_msg| DecodeError::conversion(&mode_str.literal, err_msg))?\n        } else {\n            return Err(DecodeError::missing(node, \"argument `mode` is required\"));\n        };\n\n        if let Some(surplus) = arguments.next() {\n            ctx.emit_error(DecodeError::unexpected(\n                &surplus.literal,\n                \"argument\",\n                \"unexpected argument\",\n            ))\n        }\n\n        Ok(Mode { custom, mode })\n    }\n}\n\nmacro_rules! ensure {\n    ($cond:expr, $ctx:expr, $span:expr, $fmt:literal $($arg:tt)* ) => {\n        if !$cond {\n            $ctx.emit_error(DecodeError::Conversion {\n                source: format!($fmt $($arg)*).into(),\n                span: $span.literal.span().clone()\n            });\n        }\n    };\n}\n\nimpl<S: ErrorSpan> Decode<S> for Modeline {\n    fn decode_node(node: &SpannedNode<S>, ctx: &mut Context<S>) -> Result<Self, DecodeError<S>> {\n        if let Some(type_name) = &node.type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n\n        for child in node.children() {\n            ctx.emit_error(DecodeError::unexpected(\n                child,\n                \"node\",\n                format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n            ));\n        }\n\n        for span in node.properties.keys() {\n            ctx.emit_error(DecodeError::unexpected(\n                span,\n                \"node\",\n                format!(\"unexpected node `{}`\", span.escape_default()),\n            ));\n        }\n\n        let mut arguments = node.arguments.iter();\n\n        macro_rules! m_required {\n            // This could be one identifier if macro_metavar_expr_concat stabilizes\n            ($field:ident, $value_field:ident) => {\n                let $value_field = arguments.next().ok_or_else(|| {\n                    DecodeError::missing(node, format!(\"missing {} argument\", stringify!($value)))\n                })?;\n                let $field = knuffel::traits::DecodeScalar::decode($value_field, ctx)?;\n            };\n        }\n\n        m_required!(clock, clock_value);\n        m_required!(hdisplay, hdisplay_value);\n        m_required!(hsync_start, hsync_start_value);\n        m_required!(hsync_end, hsync_end_value);\n        m_required!(htotal, htotal_value);\n        m_required!(vdisplay, vdisplay_value);\n        m_required!(vsync_start, vsync_start_value);\n        m_required!(vsync_end, vsync_end_value);\n        m_required!(vtotal, vtotal_value);\n        m_required!(hsync_polarity, hsync_polarity_value);\n        let hsync_polarity =\n            HSyncPolarity::from_str(String::as_str(&hsync_polarity)).map_err(|msg| {\n                DecodeError::Conversion {\n                    span: hsync_polarity_value.literal.span().clone(),\n                    source: msg.into(),\n                }\n            })?;\n\n        m_required!(vsync_polarity, vsync_polarity_value);\n        let vsync_polarity =\n            VSyncPolarity::from_str(String::as_str(&vsync_polarity)).map_err(|msg| {\n                DecodeError::Conversion {\n                    span: vsync_polarity_value.literal.span().clone(),\n                    source: msg.into(),\n                }\n            })?;\n\n        ensure!(\n            hdisplay < hsync_start,\n            ctx,\n            hdisplay_value,\n            \"hdisplay {} must be < hsync_start {}\",\n            hdisplay,\n            hsync_start\n        );\n        ensure!(\n            hsync_start < hsync_end,\n            ctx,\n            hsync_start_value,\n            \"hsync_start {} must be < hsync_end {}\",\n            hsync_start,\n            hsync_end,\n        );\n        ensure!(\n            hsync_end < htotal,\n            ctx,\n            hsync_end_value,\n            \"hsync_end {} must be < htotal {}\",\n            hsync_end,\n            htotal,\n        );\n        ensure!(\n            0u16 < htotal,\n            ctx,\n            htotal_value,\n            \"htotal {} must be > 0\",\n            htotal\n        );\n        ensure!(\n            vdisplay < vsync_start,\n            ctx,\n            vdisplay_value,\n            \"vdisplay {} must be < vsync_start {}\",\n            vdisplay,\n            vsync_start,\n        );\n        ensure!(\n            vsync_start < vsync_end,\n            ctx,\n            vsync_start_value,\n            \"vsync_start {} must be < vsync_end {}\",\n            vsync_start,\n            vsync_end,\n        );\n        ensure!(\n            vsync_end < vtotal,\n            ctx,\n            vsync_end_value,\n            \"vsync_end {} must be < vtotal {}\",\n            vsync_end,\n            vtotal,\n        );\n        ensure!(\n            0u16 < vtotal,\n            ctx,\n            vtotal_value,\n            \"vtotal {} must be > 0\",\n            vtotal\n        );\n\n        if let Some(extra) = arguments.next() {\n            ctx.emit_error(DecodeError::unexpected(\n                &extra.literal,\n                \"argument\",\n                \"unexpected argument, all possible arguments were already provided\",\n            ))\n        }\n\n        Ok(Modeline {\n            clock,\n            hdisplay,\n            hsync_start,\n            hsync_end,\n            htotal,\n            vdisplay,\n            vsync_start,\n            vsync_end,\n            vtotal,\n            hsync_polarity,\n            vsync_polarity,\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::assert_debug_snapshot;\n\n    use super::*;\n\n    #[test]\n    fn parse_mode() {\n        assert_eq!(\n            \"2560x1600@165.004\".parse::<ConfiguredMode>().unwrap(),\n            ConfiguredMode {\n                width: 2560,\n                height: 1600,\n                refresh: Some(165.004),\n            },\n        );\n\n        assert_eq!(\n            \"1920x1080\".parse::<ConfiguredMode>().unwrap(),\n            ConfiguredMode {\n                width: 1920,\n                height: 1080,\n                refresh: None,\n            },\n        );\n\n        assert!(\"1920\".parse::<ConfiguredMode>().is_err());\n        assert!(\"1920x\".parse::<ConfiguredMode>().is_err());\n        assert!(\"1920x1080@\".parse::<ConfiguredMode>().is_err());\n        assert!(\"1920x1080@60Hz\".parse::<ConfiguredMode>().is_err());\n    }\n\n    fn make_output_name(\n        connector: &str,\n        make: Option<&str>,\n        model: Option<&str>,\n        serial: Option<&str>,\n    ) -> OutputName {\n        OutputName {\n            connector: connector.to_string(),\n            make: make.map(|x| x.to_string()),\n            model: model.map(|x| x.to_string()),\n            serial: serial.map(|x| x.to_string()),\n        }\n    }\n\n    #[test]\n    fn test_output_name_match() {\n        fn check(\n            target: &str,\n            connector: &str,\n            make: Option<&str>,\n            model: Option<&str>,\n            serial: Option<&str>,\n        ) -> bool {\n            let name = make_output_name(connector, make, model, serial);\n            name.matches(target)\n        }\n\n        assert!(check(\"dp-2\", \"DP-2\", None, None, None));\n        assert!(!check(\"dp-1\", \"DP-2\", None, None, None));\n        assert!(check(\"dp-2\", \"DP-2\", Some(\"a\"), Some(\"b\"), Some(\"c\")));\n        assert!(check(\n            \"some company some monitor 1234\",\n            \"DP-2\",\n            Some(\"Some Company\"),\n            Some(\"Some Monitor\"),\n            Some(\"1234\")\n        ));\n        assert!(!check(\n            \"some other company some monitor 1234\",\n            \"DP-2\",\n            Some(\"Some Company\"),\n            Some(\"Some Monitor\"),\n            Some(\"1234\")\n        ));\n        assert!(!check(\n            \"make model serial \",\n            \"DP-2\",\n            Some(\"make\"),\n            Some(\"model\"),\n            Some(\"serial\")\n        ));\n        assert!(check(\n            \"make  serial\",\n            \"DP-2\",\n            Some(\"make\"),\n            Some(\"\"),\n            Some(\"serial\")\n        ));\n        assert!(check(\n            \"make model unknown\",\n            \"DP-2\",\n            Some(\"Make\"),\n            Some(\"Model\"),\n            None\n        ));\n        assert!(check(\n            \"unknown unknown serial\",\n            \"DP-2\",\n            None,\n            None,\n            Some(\"Serial\")\n        ));\n        assert!(!check(\"unknown unknown unknown\", \"DP-2\", None, None, None));\n    }\n\n    #[test]\n    fn test_output_name_sorting() {\n        let mut names = vec![\n            make_output_name(\"DP-2\", None, None, None),\n            make_output_name(\"DP-1\", None, None, None),\n            make_output_name(\"DP-3\", Some(\"B\"), Some(\"A\"), Some(\"A\")),\n            make_output_name(\"DP-3\", Some(\"A\"), Some(\"B\"), Some(\"A\")),\n            make_output_name(\"DP-3\", Some(\"A\"), Some(\"A\"), Some(\"B\")),\n            make_output_name(\"DP-3\", None, Some(\"A\"), Some(\"A\")),\n            make_output_name(\"DP-3\", Some(\"A\"), None, Some(\"A\")),\n            make_output_name(\"DP-3\", Some(\"A\"), Some(\"A\"), None),\n            make_output_name(\"DP-5\", Some(\"A\"), Some(\"A\"), Some(\"A\")),\n            make_output_name(\"DP-4\", Some(\"A\"), Some(\"A\"), Some(\"A\")),\n        ];\n        names.sort_by(|a, b| a.compare(b));\n        let names = names\n            .into_iter()\n            .map(|name| {\n                format!(\n                    \"{} | {}\",\n                    name.format_make_model_serial_or_connector(),\n                    name.connector,\n                )\n            })\n            .collect::<Vec<_>>();\n        assert_debug_snapshot!(\n            names,\n            @r#\"\n        [\n            \"Unknown A A | DP-3\",\n            \"A Unknown A | DP-3\",\n            \"A A Unknown | DP-3\",\n            \"A A A | DP-4\",\n            \"A A A | DP-5\",\n            \"A A B | DP-3\",\n            \"A B A | DP-3\",\n            \"B A A | DP-3\",\n            \"DP-1 | DP-1\",\n            \"DP-2 | DP-2\",\n        ]\n        \"#\n        );\n    }\n}\n"
  },
  {
    "path": "niri-config/src/recent_windows.rs",
    "content": "use std::collections::HashSet;\n\nuse knuffel::errors::DecodeError;\nuse smithay::input::keyboard::Keysym;\n\nuse crate::utils::{expect_only_children, MergeWith};\nuse crate::{Action, Bind, Color, FloatOrInt, Key, Modifiers, Trigger};\n\n#[derive(Debug, PartialEq)]\npub struct RecentWindows {\n    pub on: bool,\n    pub debounce_ms: u16,\n    pub open_delay_ms: u16,\n    pub highlight: MruHighlight,\n    pub previews: MruPreviews,\n    pub binds: Vec<Bind>,\n}\n\nimpl Default for RecentWindows {\n    fn default() -> Self {\n        RecentWindows {\n            on: true,\n            debounce_ms: 750,\n            open_delay_ms: 150,\n            highlight: MruHighlight::default(),\n            previews: MruPreviews::default(),\n            binds: default_binds(),\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, PartialEq)]\npub struct RecentWindowsPart {\n    #[knuffel(child)]\n    pub on: bool,\n    #[knuffel(child)]\n    pub off: bool,\n    #[knuffel(child, unwrap(argument))]\n    pub debounce_ms: Option<u16>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_delay_ms: Option<u16>,\n    #[knuffel(child)]\n    pub highlight: Option<MruHighlightPart>,\n    #[knuffel(child)]\n    pub previews: Option<MruPreviewsPart>,\n    #[knuffel(child)]\n    pub binds: Option<MruBinds>,\n}\n\nimpl MergeWith<RecentWindowsPart> for RecentWindows {\n    fn merge_with(&mut self, part: &RecentWindowsPart) {\n        self.on |= part.on;\n        if part.off {\n            self.on = false;\n        }\n\n        merge_clone!((self, part), debounce_ms, open_delay_ms);\n        merge!((self, part), highlight, previews);\n\n        if let Some(part) = &part.binds {\n            // Remove existing binds matching any new bind.\n            self.binds\n                .retain(|bind| !part.0.iter().any(|new| new.key == bind.key));\n            // Add all new binds.\n            self.binds.extend(part.0.iter().cloned().map(Bind::from));\n        }\n    }\n}\n\n#[derive(Debug, PartialEq)]\npub struct MruHighlight {\n    pub active_color: Color,\n    pub urgent_color: Color,\n    pub padding: f64,\n    pub corner_radius: f64,\n}\n\nimpl Default for MruHighlight {\n    fn default() -> Self {\n        Self {\n            active_color: Color::new_unpremul(0.6, 0.6, 0.6, 1.),\n            urgent_color: Color::new_unpremul(1., 0.6, 0.6, 1.),\n            padding: 30.,\n            corner_radius: 0.,\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, PartialEq)]\npub struct MruHighlightPart {\n    #[knuffel(child)]\n    pub active_color: Option<Color>,\n    #[knuffel(child)]\n    pub urgent_color: Option<Color>,\n    #[knuffel(child, unwrap(argument))]\n    pub padding: Option<FloatOrInt<0, 65535>>,\n    #[knuffel(child, unwrap(argument))]\n    pub corner_radius: Option<FloatOrInt<0, 65535>>,\n}\n\nimpl MergeWith<MruHighlightPart> for MruHighlight {\n    fn merge_with(&mut self, part: &MruHighlightPart) {\n        merge_clone!((self, part), active_color, urgent_color);\n        merge!((self, part), padding, corner_radius);\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct MruPreviews {\n    pub max_height: f64,\n    pub max_scale: f64,\n}\n\nimpl Default for MruPreviews {\n    fn default() -> Self {\n        Self {\n            max_height: 480.,\n            max_scale: 0.5,\n        }\n    }\n}\n\n#[derive(knuffel::Decode, Debug, Default, PartialEq)]\npub struct MruPreviewsPart {\n    #[knuffel(child, unwrap(argument))]\n    pub max_height: Option<FloatOrInt<1, 65535>>,\n    #[knuffel(child, unwrap(argument))]\n    pub max_scale: Option<FloatOrInt<0, 1>>,\n}\n\nimpl MergeWith<MruPreviewsPart> for MruPreviews {\n    fn merge_with(&mut self, part: &MruPreviewsPart) {\n        merge!((self, part), max_height, max_scale);\n    }\n}\n\n#[derive(Debug, Clone, PartialEq)]\npub struct MruBind {\n    // MRU bind keys must have a modifier, this is enforced during parsing. The switcher will close\n    // once all modifiers are released.\n    pub key: Key,\n    pub action: MruAction,\n    pub allow_inhibiting: bool,\n    pub hotkey_overlay_title: Option<Option<String>>,\n}\n\nimpl From<MruBind> for Bind {\n    fn from(x: MruBind) -> Self {\n        Self {\n            key: x.key,\n            action: Action::from(x.action),\n            repeat: true,\n            cooldown: None,\n            allow_when_locked: false,\n            allow_inhibiting: x.allow_inhibiting,\n            hotkey_overlay_title: x.hotkey_overlay_title,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\npub enum MruDirection {\n    /// Most recently used to least.\n    #[default]\n    Forward,\n    /// Least recently used to most.\n    Backward,\n}\n\n#[derive(knuffel::DecodeScalar, Clone, Copy, Debug, Default, PartialEq)]\npub enum MruScope {\n    /// All windows.\n    #[default]\n    All,\n    /// Windows on the active output.\n    Output,\n    /// Windows on the active workspace.\n    Workspace,\n}\n\n#[derive(knuffel::DecodeScalar, Clone, Copy, Debug, Default, PartialEq)]\npub enum MruFilter {\n    /// All windows.\n    #[default]\n    #[knuffel(skip)]\n    All,\n    /// Windows with the same app id as the active window.\n    AppId,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq)]\npub enum MruAction {\n    NextWindow(\n        #[knuffel(property(name = \"scope\"))] Option<MruScope>,\n        #[knuffel(property(name = \"filter\"), default)] MruFilter,\n    ),\n    PreviousWindow(\n        #[knuffel(property(name = \"scope\"))] Option<MruScope>,\n        #[knuffel(property(name = \"filter\"), default)] MruFilter,\n    ),\n}\n\nimpl From<MruAction> for Action {\n    fn from(x: MruAction) -> Self {\n        match x {\n            MruAction::NextWindow(scope, filter) => Self::MruAdvance {\n                direction: MruDirection::Forward,\n                scope,\n                filter: Some(filter),\n            },\n            MruAction::PreviousWindow(scope, filter) => Self::MruAdvance {\n                direction: MruDirection::Backward,\n                scope,\n                filter: Some(filter),\n            },\n        }\n    }\n}\n\n#[derive(Debug, Default, PartialEq)]\npub struct MruBinds(pub Vec<MruBind>);\n\nfn default_binds() -> Vec<Bind> {\n    let mut rv = Vec::new();\n\n    let mut push = |trigger, base_mod, filter| {\n        rv.push(Bind::from(MruBind {\n            key: Key {\n                trigger: Trigger::Keysym(trigger),\n                modifiers: base_mod,\n            },\n            action: MruAction::NextWindow(None, filter),\n            allow_inhibiting: true,\n            hotkey_overlay_title: None,\n        }));\n        rv.push(Bind::from(MruBind {\n            key: Key {\n                trigger: Trigger::Keysym(trigger),\n                modifiers: base_mod | Modifiers::SHIFT,\n            },\n            action: MruAction::PreviousWindow(None, filter),\n            allow_inhibiting: true,\n            hotkey_overlay_title: None,\n        }));\n    };\n\n    for base_mod in [Modifiers::ALT, Modifiers::COMPOSITOR] {\n        push(Keysym::Tab, base_mod, MruFilter::All);\n        push(Keysym::grave, base_mod, MruFilter::AppId);\n    }\n\n    rv\n}\n\nimpl<S> knuffel::Decode<S> for MruBinds\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        expect_only_children(node, ctx);\n\n        let mut seen_keys = HashSet::new();\n\n        let mut binds = Vec::new();\n\n        for child in node.children() {\n            match MruBind::decode_node(child, ctx) {\n                Ok(bind) => {\n                    if !seen_keys.insert(bind.key) {\n                        ctx.emit_error(DecodeError::unexpected(\n                            &child.node_name,\n                            \"keybind\",\n                            \"duplicate keybind\",\n                        ));\n                        continue;\n                    }\n\n                    binds.push(bind);\n                }\n                Err(e) => {\n                    ctx.emit_error(e);\n                }\n            }\n        }\n\n        Ok(Self(binds))\n    }\n}\n\nimpl<S> knuffel::Decode<S> for MruBind\nwhere\n    S: knuffel::traits::ErrorSpan,\n{\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        if let Some(type_name) = &node.type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n\n        for val in node.arguments.iter() {\n            ctx.emit_error(DecodeError::unexpected(\n                &val.literal,\n                \"argument\",\n                \"no arguments expected for this node\",\n            ));\n        }\n\n        let key = node\n            .node_name\n            .parse::<Key>()\n            .map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err(\"invalid keybind\")))?;\n\n        // A modifier is required because MRU remains on screen as long as any modifier is held.\n        if key.modifiers.is_empty() {\n            ctx.emit_error(DecodeError::unexpected(\n                &node.node_name,\n                \"keybind\",\n                \"keybind must have a modifier key\",\n            ));\n        }\n\n        // FIXME: To support this, all the mods_with_mouse_binds()/mods_with_wheel_binds()/etc.\n        // will need to learn about recent-windows bindings.\n        if !matches!(key.trigger, Trigger::Keysym(_)) {\n            ctx.emit_error(DecodeError::unexpected(\n                &node.node_name,\n                \"key\",\n                \"key must be a keyboard key (others are unsupported here for now)\",\n            ));\n        }\n\n        let mut allow_inhibiting = true;\n        let mut hotkey_overlay_title = None;\n        for (name, val) in &node.properties {\n            match &***name {\n                \"allow-inhibiting\" => {\n                    allow_inhibiting = knuffel::traits::DecodeScalar::decode(val, ctx)?;\n                }\n                \"hotkey-overlay-title\" => {\n                    hotkey_overlay_title = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);\n                }\n                name_str => {\n                    ctx.emit_error(DecodeError::unexpected(\n                        name,\n                        \"property\",\n                        format!(\"unexpected property `{}`\", name_str.escape_default()),\n                    ));\n                }\n            }\n        }\n\n        let mut children = node.children();\n\n        // If the action is invalid but the key is fine, we still want to return something.\n        // That way, the parent can handle the existence of duplicate keybinds,\n        // even if their contents are not valid.\n        let dummy = Self {\n            key,\n            action: MruAction::NextWindow(None, MruFilter::All),\n            allow_inhibiting: true,\n            hotkey_overlay_title: None,\n        };\n\n        if let Some(child) = children.next() {\n            for unwanted_child in children {\n                ctx.emit_error(DecodeError::unexpected(\n                    unwanted_child,\n                    \"node\",\n                    \"only one action is allowed per keybind\",\n                ));\n            }\n            match MruAction::decode_node(child, ctx) {\n                Ok(action) => Ok(Self {\n                    key,\n                    action,\n                    allow_inhibiting,\n                    hotkey_overlay_title,\n                }),\n                Err(e) => {\n                    ctx.emit_error(e);\n                    Ok(dummy)\n                }\n            }\n        } else {\n            ctx.emit_error(DecodeError::missing(\n                node,\n                \"expected an action for this keybind\",\n            ));\n            Ok(dummy)\n        }\n    }\n}\n"
  },
  {
    "path": "niri-config/src/utils/merge_with.rs",
    "content": "pub trait MergeWith<T> {\n    fn merge_with(&mut self, part: &T);\n\n    fn merged_with(mut self, part: &T) -> Self\n    where\n        Self: Sized,\n    {\n        self.merge_with(part);\n        self\n    }\n\n    fn from_part(part: &T) -> Self\n    where\n        Self: Default + Sized,\n    {\n        Self::default().merged_with(part)\n    }\n}\n"
  },
  {
    "path": "niri-config/src/utils.rs",
    "content": "use std::str::FromStr;\n\nuse knuffel::errors::DecodeError;\nuse miette::miette;\nuse regex::Regex;\n\nmod merge_with;\npub use merge_with::*;\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct Percent(pub f64);\n\n// MIN and MAX generics are only used during parsing to check the value.\n#[derive(Debug, Default, Clone, Copy, PartialEq)]\npub struct FloatOrInt<const MIN: i32, const MAX: i32>(pub f64);\n\n/// Flag, with an optional explicit value.\n///\n/// Intended to be used as an `Option<MaybeBool>` field, as a tri-state:\n/// - (missing): unset, `None`\n/// - just `field`: set, `Some(true)`\n/// - explicitly `field true` or `field false`: set, `Some(true)` or `Some(false)`\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq, Eq)]\npub struct Flag(#[knuffel(argument, default = true)] pub bool);\n\n/// `Regex` that implements `PartialEq` by its string form.\n#[derive(Debug, Clone)]\npub struct RegexEq(pub Regex);\n\nimpl PartialEq for RegexEq {\n    fn eq(&self, other: &Self) -> bool {\n        self.0.as_str() == other.0.as_str()\n    }\n}\n\nimpl Eq for RegexEq {}\n\nimpl FromStr for RegexEq {\n    type Err = <Regex as FromStr>::Err;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Regex::from_str(s).map(Self)\n    }\n}\n\nimpl FromStr for Percent {\n    type Err = miette::Error;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let Some((value, empty)) = s.split_once('%') else {\n            return Err(miette!(\"value must end with '%'\"));\n        };\n\n        if !empty.is_empty() {\n            return Err(miette!(\"trailing characters after '%' are not allowed\"));\n        }\n\n        let value: f64 = value.parse().map_err(|_| miette!(\"error parsing value\"))?;\n        Ok(Percent(value / 100.))\n    }\n}\n\nimpl<const MIN: i32, const MAX: i32> MergeWith<FloatOrInt<MIN, MAX>> for f64 {\n    fn merge_with(&mut self, part: &FloatOrInt<MIN, MAX>) {\n        *self = part.0;\n    }\n}\n\nimpl MergeWith<Flag> for bool {\n    fn merge_with(&mut self, part: &Flag) {\n        *self = part.0;\n    }\n}\n\nimpl<S: knuffel::traits::ErrorSpan, const MIN: i32, const MAX: i32> knuffel::DecodeScalar<S>\n    for FloatOrInt<MIN, MAX>\n{\n    fn type_check(\n        type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) {\n        if let Some(type_name) = &type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n    }\n\n    fn raw_decode(\n        val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        match &**val {\n            knuffel::ast::Literal::Int(ref value) => match value.try_into() {\n                Ok(v) => {\n                    if (MIN..=MAX).contains(&v) {\n                        Ok(FloatOrInt(f64::from(v)))\n                    } else {\n                        ctx.emit_error(DecodeError::conversion(\n                            val,\n                            format!(\"value must be between {MIN} and {MAX}\"),\n                        ));\n                        Ok(FloatOrInt::default())\n                    }\n                }\n                Err(e) => {\n                    ctx.emit_error(DecodeError::conversion(val, e));\n                    Ok(FloatOrInt::default())\n                }\n            },\n            knuffel::ast::Literal::Decimal(ref value) => match value.try_into() {\n                Ok(v) => {\n                    if (f64::from(MIN)..=f64::from(MAX)).contains(&v) {\n                        Ok(FloatOrInt(v))\n                    } else {\n                        ctx.emit_error(DecodeError::conversion(\n                            val,\n                            format!(\"value must be between {MIN} and {MAX}\"),\n                        ));\n                        Ok(FloatOrInt::default())\n                    }\n                }\n                Err(e) => {\n                    ctx.emit_error(DecodeError::conversion(val, e));\n                    Ok(FloatOrInt::default())\n                }\n            },\n            _ => {\n                ctx.emit_error(DecodeError::unsupported(\n                    val,\n                    \"Unsupported value, only numbers are recognized\",\n                ));\n                Ok(FloatOrInt::default())\n            }\n        }\n    }\n}\n\npub fn expect_only_children<S>(\n    node: &knuffel::ast::SpannedNode<S>,\n    ctx: &mut knuffel::decode::Context<S>,\n) where\n    S: knuffel::traits::ErrorSpan,\n{\n    if let Some(type_name) = &node.type_name {\n        ctx.emit_error(DecodeError::unexpected(\n            type_name,\n            \"type name\",\n            \"no type name expected for this node\",\n        ));\n    }\n\n    for val in node.arguments.iter() {\n        ctx.emit_error(DecodeError::unexpected(\n            &val.literal,\n            \"argument\",\n            \"no arguments expected for this node\",\n        ))\n    }\n\n    for name in node.properties.keys() {\n        ctx.emit_error(DecodeError::unexpected(\n            name,\n            \"property\",\n            \"no properties expected for this node\",\n        ))\n    }\n}\n\npub fn parse_arg_node<S: knuffel::traits::ErrorSpan, T: knuffel::traits::DecodeScalar<S>>(\n    name: &str,\n    node: &knuffel::ast::SpannedNode<S>,\n    ctx: &mut knuffel::decode::Context<S>,\n) -> Result<T, DecodeError<S>> {\n    let mut iter_args = node.arguments.iter();\n    let val = iter_args.next().ok_or_else(|| {\n        DecodeError::missing(node, format!(\"additional argument `{name}` is required\"))\n    })?;\n\n    let value = knuffel::traits::DecodeScalar::decode(val, ctx)?;\n\n    if let Some(val) = iter_args.next() {\n        ctx.emit_error(DecodeError::unexpected(\n            &val.literal,\n            \"argument\",\n            \"unexpected argument\",\n        ));\n    }\n    for name in node.properties.keys() {\n        ctx.emit_error(DecodeError::unexpected(\n            name,\n            \"property\",\n            format!(\"unexpected property `{}`\", name.escape_default()),\n        ));\n    }\n    for child in node.children() {\n        ctx.emit_error(DecodeError::unexpected(\n            child,\n            \"node\",\n            format!(\"unexpected node `{}`\", child.node_name.escape_default()),\n        ));\n    }\n\n    Ok(value)\n}\n"
  },
  {
    "path": "niri-config/src/window_rule.rs",
    "content": "use niri_ipc::ColumnDisplay;\n\nuse crate::appearance::{BlockOutFrom, BorderRule, CornerRadius, ShadowRule, TabIndicatorRule};\nuse crate::layout::DefaultPresetSize;\nuse crate::utils::RegexEq;\nuse crate::FloatOrInt;\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct WindowRule {\n    #[knuffel(children(name = \"match\"))]\n    pub matches: Vec<Match>,\n    #[knuffel(children(name = \"exclude\"))]\n    pub excludes: Vec<Match>,\n\n    // Rules applied at initial configure.\n    #[knuffel(child)]\n    pub default_column_width: Option<DefaultPresetSize>,\n    #[knuffel(child)]\n    pub default_window_height: Option<DefaultPresetSize>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_on_output: Option<String>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_on_workspace: Option<String>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_maximized: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_maximized_to_edges: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_fullscreen: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_floating: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub open_focused: Option<bool>,\n\n    // Rules applied dynamically.\n    #[knuffel(child, unwrap(argument))]\n    pub min_width: Option<u16>,\n    #[knuffel(child, unwrap(argument))]\n    pub min_height: Option<u16>,\n    #[knuffel(child, unwrap(argument))]\n    pub max_width: Option<u16>,\n    #[knuffel(child, unwrap(argument))]\n    pub max_height: Option<u16>,\n\n    #[knuffel(child, default)]\n    pub focus_ring: BorderRule,\n    #[knuffel(child, default)]\n    pub border: BorderRule,\n    #[knuffel(child, default)]\n    pub shadow: ShadowRule,\n    #[knuffel(child, default)]\n    pub tab_indicator: TabIndicatorRule,\n    #[knuffel(child, unwrap(argument))]\n    pub draw_border_with_background: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub opacity: Option<f32>,\n    #[knuffel(child)]\n    pub geometry_corner_radius: Option<CornerRadius>,\n    #[knuffel(child, unwrap(argument))]\n    pub clip_to_geometry: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub baba_is_float: Option<bool>,\n    #[knuffel(child, unwrap(argument))]\n    pub block_out_from: Option<BlockOutFrom>,\n    #[knuffel(child, unwrap(argument))]\n    pub variable_refresh_rate: Option<bool>,\n    #[knuffel(child, unwrap(argument, str))]\n    pub default_column_display: Option<ColumnDisplay>,\n    #[knuffel(child)]\n    pub default_floating_position: Option<FloatingPosition>,\n    #[knuffel(child, unwrap(argument))]\n    pub scroll_factor: Option<FloatOrInt<0, 100>>,\n    #[knuffel(child, unwrap(argument))]\n    pub tiled_state: Option<bool>,\n}\n\n#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]\npub struct Match {\n    #[knuffel(property, str)]\n    pub app_id: Option<RegexEq>,\n    #[knuffel(property, str)]\n    pub title: Option<RegexEq>,\n    #[knuffel(property)]\n    pub is_active: Option<bool>,\n    #[knuffel(property)]\n    pub is_focused: Option<bool>,\n    #[knuffel(property)]\n    pub is_active_in_column: Option<bool>,\n    #[knuffel(property)]\n    pub is_floating: Option<bool>,\n    #[knuffel(property)]\n    pub is_window_cast_target: Option<bool>,\n    #[knuffel(property)]\n    pub is_urgent: Option<bool>,\n    #[knuffel(property)]\n    pub at_startup: Option<bool>,\n}\n\n#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]\npub struct FloatingPosition {\n    #[knuffel(property)]\n    pub x: FloatOrInt<-65535, 65535>,\n    #[knuffel(property)]\n    pub y: FloatOrInt<-65535, 65535>,\n    #[knuffel(property, default)]\n    pub relative_to: RelativeTo,\n}\n\n#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum RelativeTo {\n    #[default]\n    TopLeft,\n    TopRight,\n    BottomLeft,\n    BottomRight,\n    Top,\n    Bottom,\n    Left,\n    Right,\n}\n"
  },
  {
    "path": "niri-config/src/workspace.rs",
    "content": "use knuffel::errors::DecodeError;\n\nuse crate::LayoutPart;\n\n#[derive(knuffel::Decode, Debug, Clone, PartialEq)]\npub struct Workspace {\n    #[knuffel(argument)]\n    pub name: WorkspaceName,\n    #[knuffel(child, unwrap(argument))]\n    pub open_on_output: Option<String>,\n    #[knuffel(child)]\n    pub layout: Option<WorkspaceLayoutPart>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct WorkspaceName(pub String);\n\n#[derive(Debug, Clone, PartialEq)]\npub struct WorkspaceLayoutPart(pub LayoutPart);\n\nimpl<S: knuffel::traits::ErrorSpan> knuffel::Decode<S> for WorkspaceLayoutPart {\n    fn decode_node(\n        node: &knuffel::ast::SpannedNode<S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<Self, DecodeError<S>> {\n        for child in node.children() {\n            let name = &**child.node_name;\n\n            // Check for disallowed properties.\n            //\n            // - empty-workspace-above-first is a monitor-level concept.\n            // - insert-hint customization could make sense for workspaces, however currently it is\n            //   also handled at the monitor level (since insert hints in-between workspaces are a\n            //   monitor-level concept), so for now this config option would do nothing.\n            if matches!(name, \"empty-workspace-above-first\" | \"insert-hint\") {\n                ctx.emit_error(DecodeError::unexpected(\n                    child,\n                    \"node\",\n                    format!(\"node `{name}` is not allowed inside `workspace.layout`\"),\n                ));\n            }\n        }\n\n        LayoutPart::decode_node(node, ctx).map(Self)\n    }\n}\n\nimpl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {\n    fn type_check(\n        type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) {\n        if let Some(type_name) = &type_name {\n            ctx.emit_error(DecodeError::unexpected(\n                type_name,\n                \"type name\",\n                \"no type name expected for this node\",\n            ));\n        }\n    }\n\n    fn raw_decode(\n        val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,\n        ctx: &mut knuffel::decode::Context<S>,\n    ) -> Result<WorkspaceName, DecodeError<S>> {\n        #[derive(Debug)]\n        struct WorkspaceNameSet(Vec<String>);\n        match &**val {\n            knuffel::ast::Literal::String(ref s) => {\n                let mut name_set: Vec<String> = match ctx.get::<WorkspaceNameSet>() {\n                    Some(h) => h.0.clone(),\n                    None => Vec::new(),\n                };\n\n                if name_set.iter().any(|name| name.eq_ignore_ascii_case(s)) {\n                    ctx.emit_error(DecodeError::unexpected(\n                        val,\n                        \"named workspace\",\n                        format!(\"duplicate named workspace: {s}\"),\n                    ));\n                    return Ok(Self(String::new()));\n                }\n\n                name_set.push(s.to_string());\n                ctx.set(WorkspaceNameSet(name_set));\n                Ok(Self(s.clone().into()))\n            }\n            _ => {\n                ctx.emit_error(DecodeError::unsupported(\n                    val,\n                    \"workspace names must be strings\",\n                ));\n                Ok(Self(String::new()))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "niri-config/tests/wiki-parses.rs",
    "content": "use std::fs;\nuse std::path::{Path, PathBuf};\n\nstruct KdlCodeBlock {\n    filename: String,\n    code: String,\n    line_number: usize,\n    must_fail: bool,\n}\n\nfn extract_kdl_from_file(file_contents: &str, filename: &str) -> Vec<KdlCodeBlock> {\n    let mut lines = file_contents\n        .lines()\n        .map(|line| {\n            // Removes the > from callouts that might contain ```kdl```\n            let line = line.trim();\n            if line.starts_with('>') {\n                if line.len() == 1 {\n                    \"\"\n                } else {\n                    &line[2..]\n                }\n            } else {\n                line\n            }\n        })\n        .enumerate();\n\n    let mut kdl_code_blocks = vec![];\n\n    while let Some((line_number, line)) = lines.next() {\n        if !line.starts_with(\"```kdl\") {\n            continue;\n        }\n\n        let mut snippet = String::new();\n\n        for (_, line) in lines\n            .by_ref()\n            .take_while(|(_, line)| !line.starts_with(\"```\"))\n        {\n            snippet.push_str(line);\n            snippet.push('\\n');\n        }\n\n        kdl_code_blocks.push(KdlCodeBlock {\n            code: snippet,\n            line_number,\n            filename: filename.to_string(),\n            must_fail: line.contains(\"must-fail\"),\n        });\n    }\n\n    kdl_code_blocks\n}\n\n#[test]\nfn wiki_docs_parses() {\n    let wiki_dir = PathBuf::from(env!(\"CARGO_MANIFEST_DIR\")).join(\"../docs/wiki\");\n\n    let code_blocks = fs::read_dir(wiki_dir)\n        .unwrap()\n        .filter_map(|entry| entry.ok())\n        .filter(|entry| entry.file_type().is_ok_and(|ft| ft.is_file()))\n        .filter(|file| {\n            file.path()\n                .extension()\n                .map(|ext| ext == \"md\")\n                .unwrap_or(false)\n        })\n        .flat_map(|file| {\n            let file_contents = fs::read_to_string(file.path()).unwrap();\n            let file_path = file.path();\n            let filename = file_path.to_str().unwrap();\n            extract_kdl_from_file(&file_contents, filename)\n        });\n\n    let mut errors = vec![];\n\n    for KdlCodeBlock {\n        code,\n        line_number,\n        filename,\n        must_fail,\n    } in code_blocks\n    {\n        if let Err(error) = niri_config::Config::parse(Path::new(&filename), &code).config {\n            if !must_fail {\n                errors.push(format!(\n                    \"Error parsing wiki KDL code block at {}:{}: {:?}\",\n                    filename,\n                    line_number,\n                    miette::Report::new(error)\n                ));\n            }\n        } else if must_fail {\n            errors.push(format!(\n                \"Expected error parsing wiki KDL code block at {filename}:{line_number}\",\n            ));\n        }\n    }\n\n    if !errors.is_empty() {\n        panic!(\n            \"Errors parsing {} wiki KDL code blocks:\\n{}\",\n            errors.len(),\n            errors.join(\"\\n\")\n        );\n    }\n}\n"
  },
  {
    "path": "niri-ipc/Cargo.toml",
    "content": "[package]\nname = \"niri-ipc\"\nversion.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrepository.workspace = true\n\ndescription = \"Types and helpers for interfacing with the niri Wayland compositor.\"\nkeywords = [\"wayland\"]\ncategories = [\"api-bindings\", \"os\"]\nreadme = \"README.md\"\n\n[dependencies]\nclap = { workspace = true, optional = true }\nschemars = { version = \"1.2.1\", optional = true }\nserde.workspace = true\nserde_json.workspace = true\n\n[features]\nclap = [\"dep:clap\"]\njson-schema = [\"dep:schemars\"]\n"
  },
  {
    "path": "niri-ipc/README.md",
    "content": "# niri-ipc\n\nTypes and helpers for interfacing with the [niri](https://github.com/niri-wm/niri) Wayland compositor.\n\n## Backwards compatibility\n\nThis crate follows the niri version.\nIt is **not** API-stable in terms of the Rust semver.\nIn particular, expect new struct fields and enum variants to be added in patch version bumps.\n\nUse an exact version requirement to avoid breaking changes:\n\n```toml\n[dependencies]\nniri-ipc = \"=25.11.0\"\n```\n"
  },
  {
    "path": "niri-ipc/src/lib.rs",
    "content": "//! Types for communicating with niri via IPC.\n//!\n//! After connecting to the niri socket, you can send [`Request`]s. Niri will process them one by\n//! one, in order, and to each request it will respond with a single [`Reply`], which is a `Result`\n//! wrapping a [`Response`].\n//!\n//! If you send a [`Request::EventStream`], niri will *stop* reading subsequent [`Request`]s, and\n//! will start continuously writing compositor [`Event`]s to the socket. If you'd like to read an\n//! event stream and write more requests at the same time, you need to use two IPC sockets.\n//!\n//! <div class=\"warning\">\n//!\n//! Requests are *always* processed separately. Time passes between requests, even when sending\n//! multiple requests to the socket at once. For example, sending [`Request::Workspaces`] and\n//! [`Request::Windows`] together may not return consistent results (e.g. a window may open on a\n//! new workspace in-between the two responses). This goes for actions too: sending\n//! [`Action::FocusWindow`] and <code>[Action::CloseWindow] { id: None }</code> together may close\n//! the wrong window because a different window got focused in-between these requests.\n//!\n//! </div>\n//!\n//! You can use the [`socket::Socket`] helper if you're fine with blocking communication. However,\n//! it is a fairly simple helper, so if you need async, or if you're using a different language,\n//! you are encouraged to communicate with the socket manually.\n//!\n//! 1. Read the socket filesystem path from [`socket::SOCKET_PATH_ENV`] (`$NIRI_SOCKET`).\n//! 2. Connect to the socket and write a JSON-formatted [`Request`] on a single line. You can follow\n//!    up with a line break and a flush, or just flush and shutdown the write end of the socket.\n//! 3. Niri will respond with a single line JSON-formatted [`Reply`].\n//! 4. You can keep writing [`Request`]s, each on a single line, and read [`Reply`]s, also each on a\n//!    separate line.\n//! 5. After you request an event stream, niri will keep responding with JSON-formatted [`Event`]s,\n//!    on a single line each.\n//!\n//! ## Backwards compatibility\n//!\n//! This crate follows the niri version. It is **not** API-stable in terms of the Rust semver. In\n//! particular, expect new struct fields and enum variants to be added in patch version bumps.\n//!\n//! Use an exact version requirement to avoid breaking changes:\n//!\n//! ```toml\n//! [dependencies]\n//! niri-ipc = \"=25.11.0\"\n//! ```\n//!\n//! ## Features\n//!\n//! This crate defines the following features:\n//! - `json-schema`: derives the [schemars](https://lib.rs/crates/schemars) `JsonSchema` trait for\n//!   the types.\n//! - `clap`: derives the clap CLI parsing traits for some types. Used internally by niri itself.\n#![warn(missing_docs)]\n\nuse std::collections::HashMap;\nuse std::str::FromStr;\nuse std::time::Duration;\n\nuse serde::{Deserialize, Serialize};\n\npub mod socket;\npub mod state;\n\n/// Request from client to niri.\n#[derive(Debug, Serialize, Deserialize, Clone)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum Request {\n    /// Request the version string for the running niri instance.\n    Version,\n    /// Request information about connected outputs.\n    Outputs,\n    /// Request information about workspaces.\n    Workspaces,\n    /// Request information about open windows.\n    Windows,\n    /// Request information about layer-shell surfaces.\n    Layers,\n    /// Request information about the configured keyboard layouts.\n    KeyboardLayouts,\n    /// Request information about the focused output.\n    FocusedOutput,\n    /// Request information about the focused window.\n    FocusedWindow,\n    /// Request picking a window and get its information.\n    PickWindow,\n    /// Request picking a color from the screen.\n    PickColor,\n    /// Perform an action.\n    Action(Action),\n    /// Change output configuration temporarily.\n    ///\n    /// The configuration is changed temporarily and not saved into the config file. If the output\n    /// configuration subsequently changes in the config file, these temporary changes will be\n    /// forgotten.\n    Output {\n        /// Output name.\n        output: String,\n        /// Configuration to apply.\n        action: OutputAction,\n    },\n    /// Start continuously receiving events from the compositor.\n    ///\n    /// The compositor should reply with `Reply::Ok(Response::Handled)`, then continuously send\n    /// [`Event`]s, one per line.\n    ///\n    /// The event stream will always give you the full current state up-front. For example, the\n    /// first workspace-related event you will receive will be [`Event::WorkspacesChanged`]\n    /// containing the full current workspaces state. You *do not* need to separately send\n    /// [`Request::Workspaces`] when using the event stream.\n    ///\n    /// Where reasonable, event stream state updates are atomic, though this is not always the\n    /// case. For example, a window may end up with a workspace id for a workspace that had already\n    /// been removed. This can happen if the corresponding [`Event::WorkspacesChanged`] arrives\n    /// before the corresponding [`Event::WindowOpenedOrChanged`].\n    EventStream,\n    /// Respond with an error (for testing error handling).\n    ReturnError,\n    /// Request information about the overview.\n    OverviewState,\n    /// Request information about screencasts.\n    Casts,\n}\n\n/// Reply from niri to client.\n///\n/// Every request gets one reply.\n///\n/// * If an error had occurred, it will be an `Reply::Err`.\n/// * If the request does not need any particular response, it will be\n///   `Reply::Ok(Response::Handled)`. Kind of like an `Ok(())`.\n/// * Otherwise, it will be `Reply::Ok(response)` with one of the other [`Response`] variants.\npub type Reply = Result<Response, String>;\n\n/// Successful response from niri to client.\n#[derive(Debug, Serialize, Deserialize, Clone)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum Response {\n    /// A request that does not need a response was handled successfully.\n    Handled,\n    /// The version string for the running niri instance.\n    Version(String),\n    /// Information about connected outputs.\n    ///\n    /// Map from output name to output info.\n    Outputs(HashMap<String, Output>),\n    /// Information about workspaces.\n    Workspaces(Vec<Workspace>),\n    /// Information about open windows.\n    Windows(Vec<Window>),\n    /// Information about layer-shell surfaces.\n    Layers(Vec<LayerSurface>),\n    /// Information about the keyboard layout.\n    KeyboardLayouts(KeyboardLayouts),\n    /// Information about the focused output.\n    FocusedOutput(Option<Output>),\n    /// Information about the focused window.\n    FocusedWindow(Option<Window>),\n    /// Information about the picked window.\n    PickedWindow(Option<Window>),\n    /// Information about the picked color.\n    PickedColor(Option<PickedColor>),\n    /// Output configuration change result.\n    OutputConfigChanged(OutputConfigChanged),\n    /// Information about the overview.\n    OverviewState(Overview),\n    /// Information about screencasts.\n    Casts(Vec<Cast>),\n}\n\n/// Overview information.\n#[derive(Serialize, Deserialize, Debug, Clone)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct Overview {\n    /// Whether the overview is currently open.\n    pub is_open: bool,\n}\n\n/// Color picked from the screen.\n#[derive(Serialize, Deserialize, Debug, Clone)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct PickedColor {\n    /// Color values as red, green, blue, each ranging from 0.0 to 1.0.\n    pub rgb: [f64; 3],\n}\n\n/// Actions that niri can perform.\n// Variants in this enum should match the spelling of the ones in niri-config. Most, but not all,\n// variants from niri-config should be present here.\n#[derive(Serialize, Deserialize, Debug, Clone)]\n#[cfg_attr(feature = \"clap\", derive(clap::Parser))]\n#[cfg_attr(feature = \"clap\", command(subcommand_value_name = \"ACTION\"))]\n#[cfg_attr(feature = \"clap\", command(subcommand_help_heading = \"Actions\"))]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum Action {\n    /// Exit niri.\n    Quit {\n        /// Skip the \"Press Enter to confirm\" prompt.\n        #[cfg_attr(feature = \"clap\", arg(short, long))]\n        skip_confirmation: bool,\n    },\n    /// Power off all monitors via DPMS.\n    PowerOffMonitors {},\n    /// Power on all monitors via DPMS.\n    PowerOnMonitors {},\n    /// Spawn a command.\n    Spawn {\n        /// Command to spawn.\n        #[cfg_attr(feature = \"clap\", arg(last = true, required = true))]\n        command: Vec<String>,\n    },\n    /// Spawn a command through the shell.\n    SpawnSh {\n        /// Command to run.\n        #[cfg_attr(feature = \"clap\", arg(last = true, required = true))]\n        command: String,\n    },\n    /// Do a screen transition.\n    DoScreenTransition {\n        /// Delay in milliseconds for the screen to freeze before starting the transition.\n        #[cfg_attr(feature = \"clap\", arg(short, long))]\n        delay_ms: Option<u16>,\n    },\n    /// Open the screenshot UI.\n    Screenshot {\n        ///  Whether to show the mouse pointer by default in the screenshot UI.\n        #[cfg_attr(feature = \"clap\", arg(short = 'p', long, action = clap::ArgAction::Set, default_value_t = true))]\n        show_pointer: bool,\n\n        /// Path to save the screenshot to.\n        ///\n        /// The path must be absolute, otherwise an error is returned.\n        ///\n        /// If `None`, the screenshot is saved according to the `screenshot-path` config setting.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set))]\n        path: Option<String>,\n    },\n    /// Screenshot the focused screen.\n    ScreenshotScreen {\n        /// Write the screenshot to disk in addition to putting it in your clipboard.\n        ///\n        /// The screenshot is saved according to the `screenshot-path` config setting.\n        #[cfg_attr(feature = \"clap\", arg(short = 'd', long, action = clap::ArgAction::Set, default_value_t = true))]\n        write_to_disk: bool,\n\n        /// Whether to include the mouse pointer in the screenshot.\n        #[cfg_attr(feature = \"clap\", arg(short = 'p', long, action = clap::ArgAction::Set, default_value_t = true))]\n        show_pointer: bool,\n\n        /// Path to save the screenshot to.\n        ///\n        /// The path must be absolute, otherwise an error is returned.\n        ///\n        /// If `None`, the screenshot is saved according to the `screenshot-path` config setting.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set))]\n        path: Option<String>,\n    },\n    /// Screenshot a window.\n    #[cfg_attr(feature = \"clap\", clap(about = \"Screenshot the focused window\"))]\n    ScreenshotWindow {\n        /// Id of the window to screenshot.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n        /// Write the screenshot to disk in addition to putting it in your clipboard.\n        ///\n        /// The screenshot is saved according to the `screenshot-path` config setting.\n        #[cfg_attr(feature = \"clap\", arg(short = 'd', long, action = clap::ArgAction::Set, default_value_t = true))]\n        write_to_disk: bool,\n\n        /// Whether to include the mouse pointer in the screenshot.\n        ///\n        /// The pointer will be included only if the window is currently receiving pointer input\n        /// (usually this means the pointer is on top of the window).\n        #[cfg_attr(feature = \"clap\", arg(short = 'p', long, action = clap::ArgAction::Set, default_value_t = false))]\n        show_pointer: bool,\n\n        /// Path to save the screenshot to.\n        ///\n        /// The path must be absolute, otherwise an error is returned.\n        ///\n        /// If `None`, the screenshot is saved according to the `screenshot-path` config setting.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set))]\n        path: Option<String>,\n    },\n    /// Enable or disable the keyboard shortcuts inhibitor (if any) for the focused surface.\n    ToggleKeyboardShortcutsInhibit {},\n    /// Close a window.\n    #[cfg_attr(feature = \"clap\", clap(about = \"Close the focused window\"))]\n    CloseWindow {\n        /// Id of the window to close.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Toggle fullscreen on a window.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Toggle fullscreen on the focused window\")\n    )]\n    FullscreenWindow {\n        /// Id of the window to toggle fullscreen of.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Toggle windowed (fake) fullscreen on a window.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Toggle windowed (fake) fullscreen on the focused window\")\n    )]\n    ToggleWindowedFullscreen {\n        /// Id of the window to toggle windowed fullscreen of.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Focus a window by id.\n    FocusWindow {\n        /// Id of the window to focus.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: u64,\n    },\n    /// Focus a window in the focused column by index.\n    FocusWindowInColumn {\n        /// Index of the window in the column.\n        ///\n        /// The index starts from 1 for the topmost window.\n        #[cfg_attr(feature = \"clap\", arg())]\n        index: u8,\n    },\n    /// Focus the previously focused window.\n    FocusWindowPrevious {},\n    /// Focus the column to the left.\n    FocusColumnLeft {},\n    /// Focus the column to the right.\n    FocusColumnRight {},\n    /// Focus the first column.\n    FocusColumnFirst {},\n    /// Focus the last column.\n    FocusColumnLast {},\n    /// Focus the next column to the right, looping if at end.\n    FocusColumnRightOrFirst {},\n    /// Focus the next column to the left, looping if at start.\n    FocusColumnLeftOrLast {},\n    /// Focus a column by index.\n    FocusColumn {\n        /// Index of the column to focus.\n        ///\n        /// The index starts from 1 for the first column.\n        #[cfg_attr(feature = \"clap\", arg())]\n        index: usize,\n    },\n    /// Focus the window or the monitor above.\n    FocusWindowOrMonitorUp {},\n    /// Focus the window or the monitor below.\n    FocusWindowOrMonitorDown {},\n    /// Focus the column or the monitor to the left.\n    FocusColumnOrMonitorLeft {},\n    /// Focus the column or the monitor to the right.\n    FocusColumnOrMonitorRight {},\n    /// Focus the window below.\n    FocusWindowDown {},\n    /// Focus the window above.\n    FocusWindowUp {},\n    /// Focus the window below or the column to the left.\n    FocusWindowDownOrColumnLeft {},\n    /// Focus the window below or the column to the right.\n    FocusWindowDownOrColumnRight {},\n    /// Focus the window above or the column to the left.\n    FocusWindowUpOrColumnLeft {},\n    /// Focus the window above or the column to the right.\n    FocusWindowUpOrColumnRight {},\n    /// Focus the window or the workspace below.\n    FocusWindowOrWorkspaceDown {},\n    /// Focus the window or the workspace above.\n    FocusWindowOrWorkspaceUp {},\n    /// Focus the topmost window.\n    FocusWindowTop {},\n    /// Focus the bottommost window.\n    FocusWindowBottom {},\n    /// Focus the window below or the topmost window.\n    FocusWindowDownOrTop {},\n    /// Focus the window above or the bottommost window.\n    FocusWindowUpOrBottom {},\n    /// Move the focused column to the left.\n    MoveColumnLeft {},\n    /// Move the focused column to the right.\n    MoveColumnRight {},\n    /// Move the focused column to the start of the workspace.\n    MoveColumnToFirst {},\n    /// Move the focused column to the end of the workspace.\n    MoveColumnToLast {},\n    /// Move the focused column to the left or to the monitor to the left.\n    MoveColumnLeftOrToMonitorLeft {},\n    /// Move the focused column to the right or to the monitor to the right.\n    MoveColumnRightOrToMonitorRight {},\n    /// Move the focused column to a specific index on its workspace.\n    MoveColumnToIndex {\n        /// New index for the column.\n        ///\n        /// The index starts from 1 for the first column.\n        #[cfg_attr(feature = \"clap\", arg())]\n        index: usize,\n    },\n    /// Move the focused window down in a column.\n    MoveWindowDown {},\n    /// Move the focused window up in a column.\n    MoveWindowUp {},\n    /// Move the focused window down in a column or to the workspace below.\n    MoveWindowDownOrToWorkspaceDown {},\n    /// Move the focused window up in a column or to the workspace above.\n    MoveWindowUpOrToWorkspaceUp {},\n    /// Consume or expel a window left.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Consume or expel the focused window left\")\n    )]\n    ConsumeOrExpelWindowLeft {\n        /// Id of the window to consume or expel.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Consume or expel a window right.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Consume or expel the focused window right\")\n    )]\n    ConsumeOrExpelWindowRight {\n        /// Id of the window to consume or expel.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Consume the window to the right into the focused column.\n    ConsumeWindowIntoColumn {},\n    /// Expel the bottom window from the focused column.\n    ExpelWindowFromColumn {},\n    /// Swap focused window with one to the right.\n    SwapWindowRight {},\n    /// Swap focused window with one to the left.\n    SwapWindowLeft {},\n    /// Toggle the focused column between normal and tabbed display.\n    ToggleColumnTabbedDisplay {},\n    /// Set the display mode of the focused column.\n    SetColumnDisplay {\n        /// Display mode to set.\n        #[cfg_attr(feature = \"clap\", arg())]\n        display: ColumnDisplay,\n    },\n    /// Center the focused column on the screen.\n    CenterColumn {},\n    /// Center a window on the screen.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Center the focused window on the screen\")\n    )]\n    CenterWindow {\n        /// Id of the window to center.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Center all fully visible columns on the screen.\n    CenterVisibleColumns {},\n    /// Focus the workspace below.\n    FocusWorkspaceDown {},\n    /// Focus the workspace above.\n    FocusWorkspaceUp {},\n    /// Focus a workspace by reference (index or name).\n    FocusWorkspace {\n        /// Reference (index or name) of the workspace to focus.\n        #[cfg_attr(feature = \"clap\", arg())]\n        reference: WorkspaceReferenceArg,\n    },\n    /// Focus the previous workspace.\n    FocusWorkspacePrevious {},\n    /// Move the focused window to the workspace below.\n    MoveWindowToWorkspaceDown {\n        /// Whether the focus should follow the target workspace.\n        ///\n        /// If `true` (the default), the focus will follow the window to the new workspace. If\n        /// `false`, the focus will remain on the original workspace.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set, default_value_t = true))]\n        focus: bool,\n    },\n    /// Move the focused window to the workspace above.\n    MoveWindowToWorkspaceUp {\n        /// Whether the focus should follow the target workspace.\n        ///\n        /// If `true` (the default), the focus will follow the window to the new workspace. If\n        /// `false`, the focus will remain on the original workspace.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set, default_value_t = true))]\n        focus: bool,\n    },\n    /// Move a window to a workspace.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Move the focused window to a workspace by reference (index or name)\")\n    )]\n    MoveWindowToWorkspace {\n        /// Id of the window to move.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        window_id: Option<u64>,\n\n        /// Reference (index or name) of the workspace to move the window to.\n        #[cfg_attr(feature = \"clap\", arg())]\n        reference: WorkspaceReferenceArg,\n\n        /// Whether the focus should follow the moved window.\n        ///\n        /// If `true` (the default) and the window to move is focused, the focus will follow the\n        /// window to the new workspace. If `false`, the focus will remain on the original\n        /// workspace.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set, default_value_t = true))]\n        focus: bool,\n    },\n    /// Move the focused column to the workspace below.\n    MoveColumnToWorkspaceDown {\n        /// Whether the focus should follow the target workspace.\n        ///\n        /// If `true` (the default), the focus will follow the column to the new workspace. If\n        /// `false`, the focus will remain on the original workspace.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set, default_value_t = true))]\n        focus: bool,\n    },\n    /// Move the focused column to the workspace above.\n    MoveColumnToWorkspaceUp {\n        /// Whether the focus should follow the target workspace.\n        ///\n        /// If `true` (the default), the focus will follow the column to the new workspace. If\n        /// `false`, the focus will remain on the original workspace.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set, default_value_t = true))]\n        focus: bool,\n    },\n    /// Move the focused column to a workspace by reference (index or name).\n    MoveColumnToWorkspace {\n        /// Reference (index or name) of the workspace to move the column to.\n        #[cfg_attr(feature = \"clap\", arg())]\n        reference: WorkspaceReferenceArg,\n\n        /// Whether the focus should follow the target workspace.\n        ///\n        /// If `true` (the default), the focus will follow the column to the new workspace. If\n        /// `false`, the focus will remain on the original workspace.\n        #[cfg_attr(feature = \"clap\", arg(long, action = clap::ArgAction::Set, default_value_t = true))]\n        focus: bool,\n    },\n    /// Move the focused workspace down.\n    MoveWorkspaceDown {},\n    /// Move the focused workspace up.\n    MoveWorkspaceUp {},\n    /// Move a workspace to a specific index on its monitor.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Move the focused workspace to a specific index on its monitor\")\n    )]\n    MoveWorkspaceToIndex {\n        /// New index for the workspace.\n        #[cfg_attr(feature = \"clap\", arg())]\n        index: usize,\n\n        /// Reference (index or name) of the workspace to move.\n        ///\n        /// If `None`, uses the focused workspace.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        reference: Option<WorkspaceReferenceArg>,\n    },\n    /// Set the name of a workspace.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Set the name of the focused workspace\")\n    )]\n    SetWorkspaceName {\n        /// New name for the workspace.\n        #[cfg_attr(feature = \"clap\", arg())]\n        name: String,\n\n        /// Reference (index or name) of the workspace to name.\n        ///\n        /// If `None`, uses the focused workspace.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        workspace: Option<WorkspaceReferenceArg>,\n    },\n    /// Unset the name of a workspace.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Unset the name of the focused workspace\")\n    )]\n    UnsetWorkspaceName {\n        /// Reference (index or name) of the workspace to unname.\n        ///\n        /// If `None`, uses the focused workspace.\n        #[cfg_attr(feature = \"clap\", arg())]\n        reference: Option<WorkspaceReferenceArg>,\n    },\n    /// Focus the monitor to the left.\n    FocusMonitorLeft {},\n    /// Focus the monitor to the right.\n    FocusMonitorRight {},\n    /// Focus the monitor below.\n    FocusMonitorDown {},\n    /// Focus the monitor above.\n    FocusMonitorUp {},\n    /// Focus the previous monitor.\n    FocusMonitorPrevious {},\n    /// Focus the next monitor.\n    FocusMonitorNext {},\n    /// Focus a monitor by name.\n    FocusMonitor {\n        /// Name of the output to focus.\n        #[cfg_attr(feature = \"clap\", arg())]\n        output: String,\n    },\n    /// Move the focused window to the monitor to the left.\n    MoveWindowToMonitorLeft {},\n    /// Move the focused window to the monitor to the right.\n    MoveWindowToMonitorRight {},\n    /// Move the focused window to the monitor below.\n    MoveWindowToMonitorDown {},\n    /// Move the focused window to the monitor above.\n    MoveWindowToMonitorUp {},\n    /// Move the focused window to the previous monitor.\n    MoveWindowToMonitorPrevious {},\n    /// Move the focused window to the next monitor.\n    MoveWindowToMonitorNext {},\n    /// Move a window to a specific monitor.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Move the focused window to a specific monitor\")\n    )]\n    MoveWindowToMonitor {\n        /// Id of the window to move.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n\n        /// The target output name.\n        #[cfg_attr(feature = \"clap\", arg())]\n        output: String,\n    },\n    /// Move the focused column to the monitor to the left.\n    MoveColumnToMonitorLeft {},\n    /// Move the focused column to the monitor to the right.\n    MoveColumnToMonitorRight {},\n    /// Move the focused column to the monitor below.\n    MoveColumnToMonitorDown {},\n    /// Move the focused column to the monitor above.\n    MoveColumnToMonitorUp {},\n    /// Move the focused column to the previous monitor.\n    MoveColumnToMonitorPrevious {},\n    /// Move the focused column to the next monitor.\n    MoveColumnToMonitorNext {},\n    /// Move the focused column to a specific monitor.\n    MoveColumnToMonitor {\n        /// The target output name.\n        #[cfg_attr(feature = \"clap\", arg())]\n        output: String,\n    },\n    /// Change the width of a window.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Change the width of the focused window\")\n    )]\n    SetWindowWidth {\n        /// Id of the window whose width to set.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n\n        /// How to change the width.\n        #[cfg_attr(feature = \"clap\", arg(allow_hyphen_values = true))]\n        change: SizeChange,\n    },\n    /// Change the height of a window.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Change the height of the focused window\")\n    )]\n    SetWindowHeight {\n        /// Id of the window whose height to set.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n\n        /// How to change the height.\n        #[cfg_attr(feature = \"clap\", arg(allow_hyphen_values = true))]\n        change: SizeChange,\n    },\n    /// Reset the height of a window back to automatic.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Reset the height of the focused window back to automatic\")\n    )]\n    ResetWindowHeight {\n        /// Id of the window whose height to reset.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Switch between preset column widths.\n    SwitchPresetColumnWidth {},\n    /// Switch between preset column widths backwards.\n    SwitchPresetColumnWidthBack {},\n    /// Switch between preset window widths.\n    SwitchPresetWindowWidth {\n        /// Id of the window whose width to switch.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Switch between preset window widths backwards.\n    SwitchPresetWindowWidthBack {\n        /// Id of the window whose width to switch.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Switch between preset window heights.\n    SwitchPresetWindowHeight {\n        /// Id of the window whose height to switch.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Switch between preset window heights backwards.\n    SwitchPresetWindowHeightBack {\n        /// Id of the window whose height to switch.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Toggle the maximized state of the focused column.\n    MaximizeColumn {},\n    /// Toggle the maximized-to-edges state of the focused window.\n    MaximizeWindowToEdges {\n        /// Id of the window to maximize.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Change the width of the focused column.\n    SetColumnWidth {\n        /// How to change the width.\n        #[cfg_attr(feature = \"clap\", arg(allow_hyphen_values = true))]\n        change: SizeChange,\n    },\n    /// Expand the focused column to space not taken up by other fully visible columns.\n    ExpandColumnToAvailableWidth {},\n    /// Switch between keyboard layouts.\n    SwitchLayout {\n        /// Layout to switch to.\n        #[cfg_attr(feature = \"clap\", arg())]\n        layout: LayoutSwitchTarget,\n    },\n    /// Show the hotkey overlay.\n    ShowHotkeyOverlay {},\n    /// Move the focused workspace to the monitor to the left.\n    MoveWorkspaceToMonitorLeft {},\n    /// Move the focused workspace to the monitor to the right.\n    MoveWorkspaceToMonitorRight {},\n    /// Move the focused workspace to the monitor below.\n    MoveWorkspaceToMonitorDown {},\n    /// Move the focused workspace to the monitor above.\n    MoveWorkspaceToMonitorUp {},\n    /// Move the focused workspace to the previous monitor.\n    MoveWorkspaceToMonitorPrevious {},\n    /// Move the focused workspace to the next monitor.\n    MoveWorkspaceToMonitorNext {},\n    /// Move a workspace to a specific monitor.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Move the focused workspace to a specific monitor\")\n    )]\n    MoveWorkspaceToMonitor {\n        /// The target output name.\n        #[cfg_attr(feature = \"clap\", arg())]\n        output: String,\n\n        // Reference (index or name) of the workspace to move.\n        ///\n        /// If `None`, uses the focused workspace.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        reference: Option<WorkspaceReferenceArg>,\n    },\n    /// Toggle a debug tint on windows.\n    ToggleDebugTint {},\n    /// Toggle visualization of render element opaque regions.\n    DebugToggleOpaqueRegions {},\n    /// Toggle visualization of output damage.\n    DebugToggleDamage {},\n    /// Move the focused window between the floating and the tiling layout.\n    ToggleWindowFloating {\n        /// Id of the window to move.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Move the focused window to the floating layout.\n    MoveWindowToFloating {\n        /// Id of the window to move.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Move the focused window to the tiling layout.\n    MoveWindowToTiling {\n        /// Id of the window to move.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Switches focus to the floating layout.\n    FocusFloating {},\n    /// Switches focus to the tiling layout.\n    FocusTiling {},\n    /// Toggles the focus between the floating and the tiling layout.\n    SwitchFocusBetweenFloatingAndTiling {},\n    /// Move a floating window on screen.\n    #[cfg_attr(feature = \"clap\", clap(about = \"Move the floating window on screen\"))]\n    MoveFloatingWindow {\n        /// Id of the window to move.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n\n        /// How to change the X position.\n        #[cfg_attr(\n            feature = \"clap\",\n            arg(short, long, default_value = \"+0\", allow_hyphen_values = true)\n        )]\n        x: PositionChange,\n\n        /// How to change the Y position.\n        #[cfg_attr(\n            feature = \"clap\",\n            arg(short, long, default_value = \"+0\", allow_hyphen_values = true)\n        )]\n        y: PositionChange,\n    },\n    /// Toggle the opacity of a window.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Toggle the opacity of the focused window\")\n    )]\n    ToggleWindowRuleOpacity {\n        /// Id of the window.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Set the dynamic cast target to a window.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Set the dynamic cast target to the focused window\")\n    )]\n    SetDynamicCastWindow {\n        /// Id of the window to target.\n        ///\n        /// If `None`, uses the focused window.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: Option<u64>,\n    },\n    /// Set the dynamic cast target to a monitor.\n    #[cfg_attr(\n        feature = \"clap\",\n        clap(about = \"Set the dynamic cast target to the focused monitor\")\n    )]\n    SetDynamicCastMonitor {\n        /// Name of the output to target.\n        ///\n        /// If `None`, uses the focused output.\n        #[cfg_attr(feature = \"clap\", arg())]\n        output: Option<String>,\n    },\n    /// Clear the dynamic cast target, making it show nothing.\n    ClearDynamicCastTarget {},\n    /// Stop a PipeWire screencast.\n    ///\n    /// wlr-screencopy screencasts cannot currently be stopped via IPC.\n    StopCast {\n        /// Session ID of the screencast to stop.\n        ///\n        /// If the session has multiple screencast streams, this will stop all of them.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        session_id: u64,\n    },\n    /// Toggle (open/close) the Overview.\n    ToggleOverview {},\n    /// Open the Overview.\n    OpenOverview {},\n    /// Close the Overview.\n    CloseOverview {},\n    /// Toggle urgent status of a window.\n    ToggleWindowUrgent {\n        /// Id of the window to toggle urgent.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: u64,\n    },\n    /// Set urgent status of a window.\n    SetWindowUrgent {\n        /// Id of the window to set urgent.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: u64,\n    },\n    /// Unset urgent status of a window.\n    UnsetWindowUrgent {\n        /// Id of the window to unset urgent.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        id: u64,\n    },\n    /// Reload the config file.\n    ///\n    /// Can be useful for scripts changing the config file, to avoid waiting the small duration for\n    /// niri's config file watcher to notice the changes.\n    LoadConfigFile {\n        /// Path of a new config file to load.\n        ///\n        /// If unset, reloads the current config file.\n        #[cfg_attr(feature = \"clap\", arg(long))]\n        path: Option<String>,\n    },\n}\n\n/// Change in window or column size.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum SizeChange {\n    /// Set the size in logical pixels.\n    SetFixed(i32),\n    /// Set the size as a proportion of the working area.\n    SetProportion(f64),\n    /// Add or subtract to the current size in logical pixels.\n    AdjustFixed(i32),\n    /// Add or subtract to the current size as a proportion of the working area.\n    AdjustProportion(f64),\n}\n\n/// Change in floating window position.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum PositionChange {\n    /// Set the position in logical pixels.\n    SetFixed(f64),\n    /// Set the position as a proportion of the working area.\n    SetProportion(f64),\n    /// Add or subtract to the current position in logical pixels.\n    AdjustFixed(f64),\n    /// Add or subtract to the current position as a proportion of the working area.\n    AdjustProportion(f64),\n}\n\n/// Workspace reference (id, index or name) to operate on.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum WorkspaceReferenceArg {\n    /// Id of the workspace.\n    Id(u64),\n    /// Index of the workspace.\n    Index(u8),\n    /// Name of the workspace.\n    Name(String),\n}\n\n/// Layout to switch to.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum LayoutSwitchTarget {\n    /// The next configured layout.\n    Next,\n    /// The previous configured layout.\n    Prev,\n    /// The specific layout by index.\n    Index(u8),\n}\n\n/// How windows display in a column.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum ColumnDisplay {\n    /// Windows are tiled vertically across the working area height.\n    Normal,\n    /// Windows are in tabs.\n    Tabbed,\n}\n\n/// Output actions that niri can perform.\n// Variants in this enum should match the spelling of the ones in niri-config. Most thigs from\n// niri-config should be present here.\n#[derive(Serialize, Deserialize, Debug, Clone)]\n#[cfg_attr(feature = \"clap\", derive(clap::Parser))]\n#[cfg_attr(feature = \"clap\", command(subcommand_value_name = \"ACTION\"))]\n#[cfg_attr(feature = \"clap\", command(subcommand_help_heading = \"Actions\"))]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum OutputAction {\n    /// Turn off the output.\n    Off,\n    /// Turn on the output.\n    On,\n    /// Set the output mode.\n    Mode {\n        /// Mode to set, or \"auto\" for automatic selection.\n        ///\n        /// Run `niri msg outputs` to see the available modes.\n        #[cfg_attr(feature = \"clap\", arg())]\n        mode: ModeToSet,\n    },\n    /// Set a custom output mode.\n    CustomMode {\n        /// Custom mode to set.\n        #[cfg_attr(feature = \"clap\", arg())]\n        mode: ConfiguredMode,\n    },\n    /// Set a custom VESA CVT modeline.\n    #[cfg_attr(feature = \"clap\", arg())]\n    Modeline {\n        /// The rate at which pixels are drawn in MHz.\n        #[cfg_attr(feature = \"clap\", arg())]\n        clock: f64,\n        /// Horizontal active pixels.\n        #[cfg_attr(feature = \"clap\", arg())]\n        hdisplay: u16,\n        /// Horizontal sync pulse start position in pixels.\n        #[cfg_attr(feature = \"clap\", arg())]\n        hsync_start: u16,\n        /// Horizontal sync pulse end position in pixels.\n        #[cfg_attr(feature = \"clap\", arg())]\n        hsync_end: u16,\n        /// Total horizontal number of pixels before resetting the horizontal drawing position to\n        /// zero.\n        #[cfg_attr(feature = \"clap\", arg())]\n        htotal: u16,\n\n        /// Vertical active pixels.\n        #[cfg_attr(feature = \"clap\", arg())]\n        vdisplay: u16,\n        /// Vertical sync pulse start position in pixels.\n        #[cfg_attr(feature = \"clap\", arg())]\n        vsync_start: u16,\n        /// Vertical sync pulse end position in pixels.\n        #[cfg_attr(feature = \"clap\", arg())]\n        vsync_end: u16,\n        /// Total vertical number of pixels before resetting the vertical drawing position to zero.\n        #[cfg_attr(feature = \"clap\", arg())]\n        vtotal: u16,\n        /// Horizontal sync polarity: \"+hsync\" or \"-hsync\".\n        #[cfg_attr(feature = \"clap\", arg(allow_hyphen_values = true))]\n        hsync_polarity: HSyncPolarity,\n        /// Vertical sync polarity: \"+vsync\" or \"-vsync\".\n        #[cfg_attr(feature = \"clap\", arg(allow_hyphen_values = true))]\n        vsync_polarity: VSyncPolarity,\n    },\n    /// Set the output scale.\n    Scale {\n        /// Scale factor to set, or \"auto\" for automatic selection.\n        #[cfg_attr(feature = \"clap\", arg())]\n        scale: ScaleToSet,\n    },\n    /// Set the output transform.\n    Transform {\n        /// Transform to set, counter-clockwise.\n        #[cfg_attr(feature = \"clap\", arg())]\n        transform: Transform,\n    },\n    /// Set the output position.\n    Position {\n        /// Position to set, or \"auto\" for automatic selection.\n        #[cfg_attr(feature = \"clap\", command(subcommand))]\n        position: PositionToSet,\n    },\n    /// Set the variable refresh rate mode.\n    Vrr {\n        /// Variable refresh rate mode to set.\n        #[cfg_attr(feature = \"clap\", command(flatten))]\n        vrr: VrrToSet,\n    },\n}\n\n/// Output mode to set.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum ModeToSet {\n    /// Niri will pick the mode automatically.\n    Automatic,\n    /// Specific mode.\n    Specific(ConfiguredMode),\n}\n\n/// Output mode as set in the config file.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct ConfiguredMode {\n    /// Width in physical pixels.\n    pub width: u16,\n    /// Height in physical pixels.\n    pub height: u16,\n    /// Refresh rate.\n    pub refresh: Option<f64>,\n}\n\n/// Modeline horizontal syncing polarity.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum HSyncPolarity {\n    /// Positive polarity.\n    PHSync,\n    /// Negative polarity.\n    NHSync,\n}\n\n/// Modeline vertical syncing polarity.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum VSyncPolarity {\n    /// Positive polarity.\n    PVSync,\n    /// Negative polarity.\n    NVSync,\n}\n\n/// Output scale to set.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum ScaleToSet {\n    /// Niri will pick the scale automatically.\n    Automatic,\n    /// Specific scale.\n    Specific(f64),\n}\n\n/// Output position to set.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"clap\", derive(clap::Subcommand))]\n#[cfg_attr(feature = \"clap\", command(subcommand_value_name = \"POSITION\"))]\n#[cfg_attr(feature = \"clap\", command(subcommand_help_heading = \"Position Values\"))]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum PositionToSet {\n    /// Position the output automatically.\n    #[cfg_attr(feature = \"clap\", command(name = \"auto\"))]\n    Automatic,\n    /// Set a specific position.\n    #[cfg_attr(feature = \"clap\", command(name = \"set\"))]\n    Specific(ConfiguredPosition),\n}\n\n/// Output position as set in the config file.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"clap\", derive(clap::Args))]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct ConfiguredPosition {\n    /// Logical X position.\n    pub x: i32,\n    /// Logical Y position.\n    pub y: i32,\n}\n\n/// Output VRR to set.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"clap\", derive(clap::Args))]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct VrrToSet {\n    /// Whether to enable variable refresh rate.\n    #[cfg_attr(\n        feature = \"clap\",\n        arg(\n            value_name = \"ON|OFF\",\n            action = clap::ArgAction::Set,\n            value_parser = clap::builder::BoolishValueParser::new(),\n            hide_possible_values = true,\n        ),\n    )]\n    pub vrr: bool,\n    /// Only enable when the output shows a window matching the variable-refresh-rate window rule.\n    #[cfg_attr(feature = \"clap\", arg(long))]\n    pub on_demand: bool,\n}\n\n/// Connected output.\n#[derive(Debug, Serialize, Deserialize, Clone)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct Output {\n    /// Name of the output.\n    pub name: String,\n    /// Textual description of the manufacturer.\n    pub make: String,\n    /// Textual description of the model.\n    pub model: String,\n    /// Serial of the output, if known.\n    pub serial: Option<String>,\n    /// Physical width and height of the output in millimeters, if known.\n    pub physical_size: Option<(u32, u32)>,\n    /// Available modes for the output.\n    pub modes: Vec<Mode>,\n    /// Index of the current mode in [`Self::modes`].\n    ///\n    /// `None` if the output is disabled.\n    pub current_mode: Option<usize>,\n    /// Whether the current_mode is a custom mode.\n    pub is_custom_mode: bool,\n    /// Whether the output supports variable refresh rate.\n    pub vrr_supported: bool,\n    /// Whether variable refresh rate is enabled on the output.\n    pub vrr_enabled: bool,\n    /// Logical output information.\n    ///\n    /// `None` if the output is not mapped to any logical output (for example, if it is disabled).\n    pub logical: Option<LogicalOutput>,\n}\n\n/// Output mode.\n#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct Mode {\n    /// Width in physical pixels.\n    pub width: u16,\n    /// Height in physical pixels.\n    pub height: u16,\n    /// Refresh rate in millihertz.\n    pub refresh_rate: u32,\n    /// Whether this mode is preferred by the monitor.\n    pub is_preferred: bool,\n}\n\n/// Logical output in the compositor's coordinate space.\n#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct LogicalOutput {\n    /// Logical X position.\n    pub x: i32,\n    /// Logical Y position.\n    pub y: i32,\n    /// Width in logical pixels.\n    pub width: u32,\n    /// Height in logical pixels.\n    pub height: u32,\n    /// Scale factor.\n    pub scale: f64,\n    /// Transform.\n    pub transform: Transform,\n}\n\n/// Output transform, which goes counter-clockwise.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"clap\", derive(clap::ValueEnum))]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum Transform {\n    /// Untransformed.\n    Normal,\n    /// Rotated by 90°.\n    #[serde(rename = \"90\")]\n    _90,\n    /// Rotated by 180°.\n    #[serde(rename = \"180\")]\n    _180,\n    /// Rotated by 270°.\n    #[serde(rename = \"270\")]\n    _270,\n    /// Flipped horizontally.\n    Flipped,\n    /// Rotated by 90° and flipped horizontally.\n    #[cfg_attr(feature = \"clap\", value(name(\"flipped-90\")))]\n    Flipped90,\n    /// Flipped vertically.\n    #[cfg_attr(feature = \"clap\", value(name(\"flipped-180\")))]\n    Flipped180,\n    /// Rotated by 270° and flipped horizontally.\n    #[cfg_attr(feature = \"clap\", value(name(\"flipped-270\")))]\n    Flipped270,\n}\n\n/// Toplevel window.\n#[derive(Serialize, Deserialize, Debug, Clone)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct Window {\n    /// Unique id of this window.\n    ///\n    /// This id remains constant while this window is open.\n    ///\n    /// Do not assume that window ids will always increase without wrapping, or start at 1. That is\n    /// an implementation detail subject to change. For example, ids may change to be randomly\n    /// generated for each new window.\n    pub id: u64,\n    /// Title, if set.\n    pub title: Option<String>,\n    /// Application ID, if set.\n    pub app_id: Option<String>,\n    /// Process ID that created the Wayland connection for this window, if known.\n    ///\n    /// Currently, windows created by xdg-desktop-portal-gnome will have a `None` PID, but this may\n    /// change in the future.\n    pub pid: Option<i32>,\n    /// Id of the workspace this window is on, if any.\n    pub workspace_id: Option<u64>,\n    /// Whether this window is currently focused.\n    ///\n    /// There can be either one focused window or zero (e.g. when a layer-shell surface has focus).\n    pub is_focused: bool,\n    /// Whether this window is currently floating.\n    ///\n    /// If the window isn't floating then it is in the tiling layout.\n    pub is_floating: bool,\n    /// Whether this window requests your attention.\n    pub is_urgent: bool,\n    /// Position- and size-related properties of the window.\n    pub layout: WindowLayout,\n    /// Timestamp when the window was most recently focused.\n    ///\n    /// This timestamp is intended for most-recently-used window switchers, i.e. Alt-Tab. It only\n    /// updates after some debounce time so that quick window switching doesn't mark intermediate\n    /// windows as recently focused.\n    ///\n    /// The timestamp comes from the monotonic clock.\n    pub focus_timestamp: Option<Timestamp>,\n}\n\n/// A moment in time.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct Timestamp {\n    /// Number of whole seconds.\n    pub secs: u64,\n    /// Fractional part of the timestamp in nanoseconds (10<sup>-9</sup> seconds).\n    pub nanos: u32,\n}\n\n/// Position- and size-related properties of a [`Window`].\n///\n/// Optional properties will be unset for some windows, do not rely on them being present. Whether\n/// some optional properties are present or absent for certain window types may change across niri\n/// releases.\n///\n/// All sizes and positions are in *logical pixels* unless stated otherwise. Logical sizes may be\n/// fractional. For example, at 1.25 monitor scale, a 2-physical-pixel-wide window border is 1.6\n/// logical pixels wide.\n///\n/// This struct contains positions and sizes both for full tiles ([`Self::tile_size`],\n/// [`Self::tile_pos_in_workspace_view`]) and the window geometry ([`Self::window_size`],\n/// [`Self::window_offset_in_tile`]). For visual displays, use the tile properties, as they\n/// correspond to what the user visually considers \"window\". The window properties on the other\n/// hand are mainly useful when you need to know the underlying Wayland window sizes, e.g. for\n/// application debugging.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct WindowLayout {\n    /// Location of a tiled window within a workspace: (column index, tile index in column).\n    ///\n    /// The indices are 1-based, i.e. the leftmost column is at index 1 and the topmost tile in a\n    /// column is at index 1. This is consistent with [`Action::FocusColumn`] and\n    /// [`Action::FocusWindowInColumn`].\n    pub pos_in_scrolling_layout: Option<(usize, usize)>,\n    /// Size of the tile this window is in, including decorations like borders.\n    pub tile_size: (f64, f64),\n    /// Size of the window's visual geometry itself.\n    ///\n    /// Does not include niri decorations like borders.\n    ///\n    /// Currently, Wayland toplevel windows can only be integer-sized in logical pixels, even\n    /// though it doesn't necessarily align to physical pixels.\n    pub window_size: (i32, i32),\n    /// Tile position within the current view of the workspace.\n    ///\n    /// This is the same \"workspace view\" as in gradients' `relative-to` in the niri config.\n    pub tile_pos_in_workspace_view: Option<(f64, f64)>,\n    /// Location of the window's visual geometry within its tile.\n    ///\n    /// This includes things like border sizes. For fullscreened fixed-size windows this includes\n    /// the distance from the corner of the black backdrop to the corner of the (centered) window\n    /// contents.\n    pub window_offset_in_tile: (f64, f64),\n}\n\n/// Output configuration change result.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum OutputConfigChanged {\n    /// The target output was connected and the change was applied.\n    Applied,\n    /// The target output was not found, the change will be applied when it is connected.\n    OutputWasMissing,\n}\n\n/// A workspace.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct Workspace {\n    /// Unique id of this workspace.\n    ///\n    /// This id remains constant regardless of the workspace moving around and across monitors.\n    ///\n    /// Do not assume that workspace ids will always increase without wrapping, or start at 1. That\n    /// is an implementation detail subject to change. For example, ids may change to be randomly\n    /// generated for each new workspace.\n    pub id: u64,\n    /// Index of the workspace on its monitor.\n    ///\n    /// This is the same index you can use for requests like `niri msg action focus-workspace`.\n    ///\n    /// This index *will change* as you move and re-order workspace. It is merely the workspace's\n    /// current position on its monitor. Workspaces on different monitors can have the same index.\n    ///\n    /// If you need a unique workspace id that doesn't change, see [`Self::id`].\n    pub idx: u8,\n    /// Optional name of the workspace.\n    pub name: Option<String>,\n    /// Name of the output that the workspace is on.\n    ///\n    /// Can be `None` if no outputs are currently connected.\n    pub output: Option<String>,\n    /// Whether the workspace currently has an urgent window in its output.\n    pub is_urgent: bool,\n    /// Whether the workspace is currently active on its output.\n    ///\n    /// Every output has one active workspace, the one that is currently visible on that output.\n    pub is_active: bool,\n    /// Whether the workspace is currently focused.\n    ///\n    /// There's only one focused workspace across all outputs.\n    pub is_focused: bool,\n    /// Id of the active window on this workspace, if any.\n    pub active_window_id: Option<u64>,\n}\n\n/// Configured keyboard layouts.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct KeyboardLayouts {\n    /// XKB names of the configured layouts.\n    pub names: Vec<String>,\n    /// Index of the currently active layout in `names`.\n    pub current_idx: u8,\n}\n\n/// A layer-shell layer.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum Layer {\n    /// The background layer.\n    Background,\n    /// The bottom layer.\n    Bottom,\n    /// The top layer.\n    Top,\n    /// The overlay layer.\n    Overlay,\n}\n\n/// Keyboard interactivity modes for a layer-shell surface.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum LayerSurfaceKeyboardInteractivity {\n    /// Surface cannot receive keyboard focus.\n    None,\n    /// Surface receives keyboard focus whenever possible.\n    Exclusive,\n    /// Surface receives keyboard focus on demand, e.g. when clicked.\n    OnDemand,\n}\n\n/// A layer-shell surface.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct LayerSurface {\n    /// Namespace provided by the layer-shell client.\n    pub namespace: String,\n    /// Name of the output the surface is on.\n    pub output: String,\n    /// Layer that the surface is on.\n    pub layer: Layer,\n    /// The surface's keyboard interactivity mode.\n    pub keyboard_interactivity: LayerSurfaceKeyboardInteractivity,\n}\n\n/// A screencast.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub struct Cast {\n    /// Stream ID of the screencast that uniquely identifies it.\n    pub stream_id: u64,\n    /// Session ID of the screencast.\n    ///\n    /// A session can have multiple screencast streams. Then multiple `Cast`s will have the same\n    /// `session_id`. Though, usually there's only one stream per session.\n    ///\n    /// Do not confuse `session_id` with [`stream_id`](Self::stream_id).\n    pub session_id: u64,\n    /// Kind of this screencast.\n    pub kind: CastKind,\n    /// Target being captured.\n    pub target: CastTarget,\n    /// Whether this is a Dynamic Cast Target screencast.\n    ///\n    /// Meaning that actions like `SetDynamicCastWindow` will act on this screencast.\n    ///\n    /// Keep in mind that the target can change even if this is `false`.\n    pub is_dynamic_target: bool,\n    /// Whether the cast is currently streaming frames.\n    ///\n    /// This can be `false` for example when switching away to a different scene in OBS, which\n    /// pauses the stream.\n    pub is_active: bool,\n    /// Process ID of the screencast consumer, if known.\n    ///\n    /// Currently, only wlr-screencopy screencasts can have a pid.\n    pub pid: Option<i32>,\n    /// PipeWire node ID of the screencast stream.\n    ///\n    /// This is `None` for wlr-screencopy casts, and also for PipeWire casts before the node is\n    /// created (when the cast is just starting up).\n    pub pw_node_id: Option<u32>,\n}\n\n/// Kind of screencast.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum CastKind {\n    /// PipeWire screencast, typically via xdg-desktop-portal-gnome.\n    PipeWire,\n    /// wlr-screencopy protocol screencast.\n    ///\n    /// Tools like wf-recorder, and the xdg-desktop-portal-wlr portal.\n    ///\n    /// Only wlr-screencopy with damage tracking is reported here. Screencopy without damage is\n    /// treated as a regular screenshot and not reported as a screencast.\n    WlrScreencopy,\n}\n\n/// Target of a screencast.\n#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum CastTarget {\n    /// The target is not yet set, or was cleared.\n    Nothing {},\n    /// Casting an output.\n    Output {\n        /// Name of the screencasted output.\n        name: String,\n    },\n    /// Casting a window.\n    Window {\n        /// ID of the screencasted window.\n        id: u64,\n    },\n}\n\n/// A compositor event.\n#[derive(Serialize, Deserialize, Debug, Clone)]\n#[cfg_attr(feature = \"json-schema\", derive(schemars::JsonSchema))]\npub enum Event {\n    /// The workspace configuration has changed.\n    WorkspacesChanged {\n        /// The new workspace configuration.\n        ///\n        /// This configuration completely replaces the previous configuration. I.e. if any\n        /// workspaces are missing from here, then they were deleted.\n        workspaces: Vec<Workspace>,\n    },\n    /// The workspace urgency changed.\n    WorkspaceUrgencyChanged {\n        /// Id of the workspace.\n        id: u64,\n        /// Whether this workspace has an urgent window.\n        urgent: bool,\n    },\n    /// A workspace was activated on an output.\n    ///\n    /// This doesn't always mean the workspace became focused, just that it's now the active\n    /// workspace on its output. All other workspaces on the same output become inactive.\n    WorkspaceActivated {\n        /// Id of the newly active workspace.\n        id: u64,\n        /// Whether this workspace also became focused.\n        ///\n        /// If `true`, this is now the single focused workspace. All other workspaces are no longer\n        /// focused, but they may remain active on their respective outputs.\n        focused: bool,\n    },\n    /// An active window changed on a workspace.\n    WorkspaceActiveWindowChanged {\n        /// Id of the workspace on which the active window changed.\n        workspace_id: u64,\n        /// Id of the new active window, if any.\n        active_window_id: Option<u64>,\n    },\n    /// The window configuration has changed.\n    WindowsChanged {\n        /// The new window configuration.\n        ///\n        /// This configuration completely replaces the previous configuration. I.e. if any windows\n        /// are missing from here, then they were closed.\n        windows: Vec<Window>,\n    },\n    /// A new toplevel window was opened, or an existing toplevel window changed.\n    WindowOpenedOrChanged {\n        /// The new or updated window.\n        ///\n        /// If the window is focused, all other windows are no longer focused.\n        window: Window,\n    },\n    /// A toplevel window was closed.\n    WindowClosed {\n        /// Id of the removed window.\n        id: u64,\n    },\n    /// Window focus changed.\n    ///\n    /// All other windows are no longer focused.\n    WindowFocusChanged {\n        /// Id of the newly focused window, or `None` if no window is now focused.\n        id: Option<u64>,\n    },\n    /// Window focus timestamp changed.\n    ///\n    /// This event is separate from [`Event::WindowFocusChanged`] because the focus timestamp only\n    /// updates after some debounce time so that quick window switching doesn't mark intermediate\n    /// windows as recently focused.\n    WindowFocusTimestampChanged {\n        /// Id of the window.\n        id: u64,\n        /// The new focus timestamp.\n        focus_timestamp: Option<Timestamp>,\n    },\n    /// Window urgency changed.\n    WindowUrgencyChanged {\n        /// Id of the window.\n        id: u64,\n        /// The new urgency state of the window.\n        urgent: bool,\n    },\n    /// The layout of one or more windows has changed.\n    WindowLayoutsChanged {\n        /// Pairs consisting of a window id and new layout information for the window.\n        changes: Vec<(u64, WindowLayout)>,\n    },\n    /// The configured keyboard layouts have changed.\n    KeyboardLayoutsChanged {\n        /// The new keyboard layout configuration.\n        keyboard_layouts: KeyboardLayouts,\n    },\n    /// The keyboard layout switched.\n    KeyboardLayoutSwitched {\n        /// Index of the newly active layout.\n        idx: u8,\n    },\n    /// The overview was opened or closed.\n    OverviewOpenedOrClosed {\n        /// The new state of the overview.\n        is_open: bool,\n    },\n    /// The configuration was reloaded.\n    ///\n    /// You will always receive this event when connecting to the event stream, indicating the last\n    /// config load attempt.\n    ConfigLoaded {\n        /// Whether the loading failed.\n        ///\n        /// For example, the config file couldn't be parsed.\n        failed: bool,\n    },\n    /// A screenshot was captured.\n    ScreenshotCaptured {\n        /// The file path where the screenshot was saved, if it was written to disk.\n        ///\n        /// If `None`, the screenshot was either only copied to the clipboard, or the path couldn't\n        /// be converted to a `String` (e.g. contained invalid UTF-8 bytes).\n        path: Option<String>,\n    },\n    /// The screencasts have changed.\n    CastsChanged {\n        /// The new screencast information.\n        ///\n        /// This configuration completely replaces the previous configuration. I.e. if any casts\n        /// are missing from here, then they were stopped.\n        casts: Vec<Cast>,\n    },\n    /// A screencast started, or an existing cast changed.\n    CastStartedOrChanged {\n        /// The cast that started or changed.\n        cast: Cast,\n    },\n    /// A screencast stopped.\n    CastStopped {\n        /// Stream ID of the stopped screencast.\n        stream_id: u64,\n    },\n}\n\nimpl From<Duration> for Timestamp {\n    fn from(value: Duration) -> Self {\n        Timestamp {\n            secs: value.as_secs(),\n            nanos: value.subsec_nanos(),\n        }\n    }\n}\n\nimpl From<Timestamp> for Duration {\n    fn from(value: Timestamp) -> Self {\n        Duration::new(value.secs, value.nanos)\n    }\n}\n\nimpl FromStr for WorkspaceReferenceArg {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let reference = if let Ok(index) = s.parse::<i32>() {\n            if let Ok(idx) = u8::try_from(index) {\n                Self::Index(idx)\n            } else {\n                return Err(\"workspace index must be between 0 and 255\");\n            }\n        } else {\n            Self::Name(s.to_string())\n        };\n\n        Ok(reference)\n    }\n}\n\nimpl FromStr for SizeChange {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s.split_once('%') {\n            Some((value, empty)) => {\n                if !empty.is_empty() {\n                    return Err(\"trailing characters after '%' are not allowed\");\n                }\n\n                match value.bytes().next() {\n                    Some(b'-' | b'+') => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::AdjustProportion(value))\n                    }\n                    Some(_) => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::SetProportion(value))\n                    }\n                    None => Err(\"value is missing\"),\n                }\n            }\n            None => {\n                let value = s;\n                match value.bytes().next() {\n                    Some(b'-' | b'+') => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::AdjustFixed(value))\n                    }\n                    Some(_) => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::SetFixed(value))\n                    }\n                    None => Err(\"value is missing\"),\n                }\n            }\n        }\n    }\n}\n\nimpl FromStr for PositionChange {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s.split_once('%') {\n            Some((value, empty)) => {\n                if !empty.is_empty() {\n                    return Err(\"trailing characters after '%' are not allowed\");\n                }\n\n                match value.bytes().next() {\n                    Some(b'-' | b'+') => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::AdjustProportion(value))\n                    }\n                    Some(_) => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::SetProportion(value))\n                    }\n                    None => Err(\"value is missing\"),\n                }\n            }\n            None => {\n                let value = s;\n                match value.bytes().next() {\n                    Some(b'-' | b'+') => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::AdjustFixed(value))\n                    }\n                    Some(_) => {\n                        let value = value.parse().map_err(|_| \"error parsing value\")?;\n                        Ok(Self::SetFixed(value))\n                    }\n                    None => Err(\"value is missing\"),\n                }\n            }\n        }\n    }\n}\n\nimpl FromStr for LayoutSwitchTarget {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"next\" => Ok(Self::Next),\n            \"prev\" => Ok(Self::Prev),\n            other => match other.parse() {\n                Ok(layout) => Ok(Self::Index(layout)),\n                _ => Err(r#\"invalid layout action, can be \"next\", \"prev\" or a layout index\"#),\n            },\n        }\n    }\n}\n\nimpl FromStr for ColumnDisplay {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"normal\" => Ok(Self::Normal),\n            \"tabbed\" => Ok(Self::Tabbed),\n            _ => Err(r#\"invalid column display, can be \"normal\" or \"tabbed\"\"#),\n        }\n    }\n}\n\nimpl FromStr for Transform {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"normal\" => Ok(Self::Normal),\n            \"90\" => Ok(Self::_90),\n            \"180\" => Ok(Self::_180),\n            \"270\" => Ok(Self::_270),\n            \"flipped\" => Ok(Self::Flipped),\n            \"flipped-90\" => Ok(Self::Flipped90),\n            \"flipped-180\" => Ok(Self::Flipped180),\n            \"flipped-270\" => Ok(Self::Flipped270),\n            _ => Err(concat!(\n                r#\"invalid transform, can be \"90\", \"180\", \"270\", \"#,\n                r#\"\"flipped\", \"flipped-90\", \"flipped-180\" or \"flipped-270\"\"#\n            )),\n        }\n    }\n}\n\nimpl FromStr for ModeToSet {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        if s.eq_ignore_ascii_case(\"auto\") {\n            return Ok(Self::Automatic);\n        }\n\n        let mode = s.parse()?;\n        Ok(Self::Specific(mode))\n    }\n}\n\nimpl FromStr for ConfiguredMode {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let Some((width, rest)) = s.split_once('x') else {\n            return Err(\"no 'x' separator found\");\n        };\n\n        let (height, refresh) = match rest.split_once('@') {\n            Some((height, refresh)) => (height, Some(refresh)),\n            None => (rest, None),\n        };\n\n        let width = width.parse().map_err(|_| \"error parsing width\")?;\n        let height = height.parse().map_err(|_| \"error parsing height\")?;\n        let refresh = refresh\n            .map(str::parse)\n            .transpose()\n            .map_err(|_| \"error parsing refresh rate\")?;\n\n        Ok(Self {\n            width,\n            height,\n            refresh,\n        })\n    }\n}\n\nimpl FromStr for HSyncPolarity {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"+hsync\" => Ok(Self::PHSync),\n            \"-hsync\" => Ok(Self::NHSync),\n            _ => Err(r#\"invalid horizontal sync polarity, can be \"+hsync\" or \"-hsync\"#),\n        }\n    }\n}\n\nimpl FromStr for VSyncPolarity {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"+vsync\" => Ok(Self::PVSync),\n            \"-vsync\" => Ok(Self::NVSync),\n            _ => Err(r#\"invalid vertical sync polarity, can be \"+vsync\" or \"-vsync\"#),\n        }\n    }\n}\n\nimpl FromStr for ScaleToSet {\n    type Err = &'static str;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        if s.eq_ignore_ascii_case(\"auto\") {\n            return Ok(Self::Automatic);\n        }\n\n        let scale = s.parse().map_err(|_| \"error parsing scale\")?;\n        Ok(Self::Specific(scale))\n    }\n}\n\nmacro_rules! ensure {\n    ($cond:expr, $fmt:literal $($arg:tt)* ) => {\n        if !$cond {\n            return Err(format!($fmt $($arg)*));\n        }\n    };\n}\n\nimpl OutputAction {\n    /// Validates some required constraints on the modeline and custom mode.\n    pub fn validate(&self) -> Result<(), String> {\n        match self {\n            OutputAction::Modeline {\n                hdisplay,\n                hsync_start,\n                hsync_end,\n                htotal,\n                vdisplay,\n                vsync_start,\n                vsync_end,\n                vtotal,\n                ..\n            } => {\n                ensure!(\n                    hdisplay < hsync_start,\n                    \"hdisplay {} must be < hsync_start {}\",\n                    hdisplay,\n                    hsync_start\n                );\n                ensure!(\n                    hsync_start < hsync_end,\n                    \"hsync_start {} must be < hsync_end {}\",\n                    hsync_start,\n                    hsync_end\n                );\n                ensure!(\n                    hsync_end < htotal,\n                    \"hsync_end {} must be < htotal {}\",\n                    hsync_end,\n                    htotal\n                );\n                ensure!(0 < *htotal, \"htotal {} must be > 0\", htotal);\n                ensure!(\n                    vdisplay < vsync_start,\n                    \"vdisplay {} must be < vsync_start {}\",\n                    vdisplay,\n                    vsync_start\n                );\n                ensure!(\n                    vsync_start < vsync_end,\n                    \"vsync_start {} must be < vsync_end {}\",\n                    vsync_start,\n                    vsync_end\n                );\n                ensure!(\n                    vsync_end < vtotal,\n                    \"vsync_end {} must be < vtotal {}\",\n                    vsync_end,\n                    vtotal\n                );\n                ensure!(0 < *vtotal, \"vtotal {} must be > 0\", vtotal);\n                Ok(())\n            }\n            OutputAction::CustomMode {\n                mode: ConfiguredMode { refresh, .. },\n            } => {\n                if refresh.is_none() {\n                    return Err(\"refresh rate is required for custom modes\".to_string());\n                }\n                if let Some(refresh) = refresh {\n                    if *refresh <= 0. {\n                        return Err(format!(\"custom mode refresh rate {refresh} must be > 0\"));\n                    }\n                }\n                Ok(())\n            }\n            _ => Ok(()),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn parse_size_change() {\n        assert_eq!(\n            \"10\".parse::<SizeChange>().unwrap(),\n            SizeChange::SetFixed(10),\n        );\n        assert_eq!(\n            \"+10\".parse::<SizeChange>().unwrap(),\n            SizeChange::AdjustFixed(10),\n        );\n        assert_eq!(\n            \"-10\".parse::<SizeChange>().unwrap(),\n            SizeChange::AdjustFixed(-10),\n        );\n        assert_eq!(\n            \"10%\".parse::<SizeChange>().unwrap(),\n            SizeChange::SetProportion(10.),\n        );\n        assert_eq!(\n            \"+10%\".parse::<SizeChange>().unwrap(),\n            SizeChange::AdjustProportion(10.),\n        );\n        assert_eq!(\n            \"-10%\".parse::<SizeChange>().unwrap(),\n            SizeChange::AdjustProportion(-10.),\n        );\n\n        assert!(\"-\".parse::<SizeChange>().is_err());\n        assert!(\"10% \".parse::<SizeChange>().is_err());\n    }\n\n    #[test]\n    fn parse_position_change() {\n        assert_eq!(\n            \"10\".parse::<PositionChange>().unwrap(),\n            PositionChange::SetFixed(10.),\n        );\n        assert_eq!(\n            \"+10\".parse::<PositionChange>().unwrap(),\n            PositionChange::AdjustFixed(10.),\n        );\n        assert_eq!(\n            \"-10\".parse::<PositionChange>().unwrap(),\n            PositionChange::AdjustFixed(-10.),\n        );\n\n        assert_eq!(\n            \"10%\".parse::<PositionChange>().unwrap(),\n            PositionChange::SetProportion(10.)\n        );\n        assert_eq!(\n            \"+10%\".parse::<PositionChange>().unwrap(),\n            PositionChange::AdjustProportion(10.)\n        );\n        assert_eq!(\n            \"-10%\".parse::<PositionChange>().unwrap(),\n            PositionChange::AdjustProportion(-10.)\n        );\n        assert!(\"-\".parse::<PositionChange>().is_err());\n        assert!(\"10% \".parse::<PositionChange>().is_err());\n    }\n}\n"
  },
  {
    "path": "niri-ipc/src/socket.rs",
    "content": "//! Helper for blocking communication over the niri socket.\n\nuse std::env;\nuse std::io::{self, BufRead, BufReader, Write};\nuse std::net::Shutdown;\nuse std::os::unix::net::UnixStream;\nuse std::path::Path;\n\nuse crate::{Event, Reply, Request};\n\n/// Name of the environment variable containing the niri IPC socket path.\npub const SOCKET_PATH_ENV: &str = \"NIRI_SOCKET\";\n\n/// Helper for blocking communication over the niri socket.\n///\n/// This struct is used to communicate with the niri IPC server. It handles the socket connection\n/// and serialization/deserialization of messages.\npub struct Socket {\n    stream: BufReader<UnixStream>,\n}\n\nimpl Socket {\n    /// Connects to the default niri IPC socket.\n    ///\n    /// This is equivalent to calling [`Self::connect_to`] with the path taken from the\n    /// [`SOCKET_PATH_ENV`] environment variable.\n    pub fn connect() -> io::Result<Self> {\n        let socket_path = env::var_os(SOCKET_PATH_ENV).ok_or_else(|| {\n            io::Error::new(\n                io::ErrorKind::NotFound,\n                format!(\"{SOCKET_PATH_ENV} is not set, are you running this within niri?\"),\n            )\n        })?;\n        Self::connect_to(socket_path)\n    }\n\n    /// Connects to the niri IPC socket at the given path.\n    pub fn connect_to(path: impl AsRef<Path>) -> io::Result<Self> {\n        let stream = UnixStream::connect(path.as_ref())?;\n        let stream = BufReader::new(stream);\n        Ok(Self { stream })\n    }\n\n    /// Sends a request to niri and returns the response.\n    ///\n    /// Return values:\n    ///\n    /// * `Ok(Ok(response))`: successful [`Response`](crate::Response) from niri\n    /// * `Ok(Err(message))`: error message from niri\n    /// * `Err(error)`: error communicating with niri\n    pub fn send(&mut self, request: Request) -> io::Result<Reply> {\n        let mut buf = serde_json::to_string(&request).unwrap();\n        buf.push('\\n');\n        self.stream.get_mut().write_all(buf.as_bytes())?;\n\n        buf.clear();\n        self.stream.read_line(&mut buf)?;\n\n        let reply = serde_json::from_str(&buf)?;\n        Ok(reply)\n    }\n\n    /// Starts reading event stream [`Event`]s from the socket.\n    ///\n    /// The returned function will block until the next [`Event`] arrives, then return it.\n    ///\n    /// Use this only after requesting an [`EventStream`][Request::EventStream].\n    ///\n    /// # Examples\n    ///\n    /// ```no_run\n    /// use niri_ipc::{Request, Response};\n    /// use niri_ipc::socket::Socket;\n    ///\n    /// fn main() -> std::io::Result<()> {\n    ///     let mut socket = Socket::connect()?;\n    ///\n    ///     let reply = socket.send(Request::EventStream)?;\n    ///     if matches!(reply, Ok(Response::Handled)) {\n    ///         let mut read_event = socket.read_events();\n    ///         while let Ok(event) = read_event() {\n    ///             println!(\"Received event: {event:?}\");\n    ///         }\n    ///     }\n    ///\n    ///     Ok(())\n    /// }\n    /// ```\n    pub fn read_events(self) -> impl FnMut() -> io::Result<Event> {\n        let Self { mut stream } = self;\n        let _ = stream.get_mut().shutdown(Shutdown::Write);\n\n        let mut buf = String::new();\n        move || {\n            buf.clear();\n            stream.read_line(&mut buf)?;\n            let event = serde_json::from_str(&buf)?;\n            Ok(event)\n        }\n    }\n}\n"
  },
  {
    "path": "niri-ipc/src/state.rs",
    "content": "//! Helpers for keeping track of the event stream state.\n//!\n//! 1. Create an [`EventStreamState`] using `Default::default()`, or any individual state part if\n//!    you only care about part of the state.\n//! 2. Connect to the niri socket and request an event stream.\n//! 3. Pass every [`Event`] to [`EventStreamStatePart::apply`] on your state.\n//! 4. Read the fields of the state as needed.\n\nuse std::collections::hash_map::Entry;\nuse std::collections::HashMap;\n\nuse crate::{Cast, Event, KeyboardLayouts, Window, Workspace};\n\n/// Part of the state communicated via the event stream.\npub trait EventStreamStatePart {\n    /// Returns a sequence of events that replicates this state from default initialization.\n    fn replicate(&self) -> Vec<Event>;\n\n    /// Applies the event to this state.\n    ///\n    /// Returns `None` after applying the event, and `Some(event)` if the event is ignored by this\n    /// part of the state.\n    fn apply(&mut self, event: Event) -> Option<Event>;\n}\n\n/// The full state communicated over the event stream.\n///\n/// Different parts of the state are not guaranteed to be consistent across every single event\n/// sent by niri. For example, you may receive the first [`Event::WindowOpenedOrChanged`] for a\n/// just-opened window *after* an [`Event::WorkspaceActiveWindowChanged`] for that window. Between\n/// these two events, the workspace active window id refers to a window that does not yet exist in\n/// the windows state part.\n#[derive(Debug, Default)]\npub struct EventStreamState {\n    /// State of workspaces.\n    pub workspaces: WorkspacesState,\n\n    /// State of workspaces.\n    pub windows: WindowsState,\n\n    /// State of the keyboard layouts.\n    pub keyboard_layouts: KeyboardLayoutsState,\n\n    /// State of the overview.\n    pub overview: OverviewState,\n\n    /// State of the config.\n    pub config: ConfigState,\n\n    /// State of screencasts.\n    pub casts: CastsState,\n}\n\n/// The workspaces state communicated over the event stream.\n#[derive(Debug, Default)]\npub struct WorkspacesState {\n    /// Map from a workspace id to the workspace.\n    pub workspaces: HashMap<u64, Workspace>,\n}\n\n/// The windows state communicated over the event stream.\n#[derive(Debug, Default)]\npub struct WindowsState {\n    /// Map from a window id to the window.\n    pub windows: HashMap<u64, Window>,\n}\n\n/// The keyboard layout state communicated over the event stream.\n#[derive(Debug, Default)]\npub struct KeyboardLayoutsState {\n    /// Configured keyboard layouts.\n    pub keyboard_layouts: Option<KeyboardLayouts>,\n}\n\n/// The overview state communicated over the event stream.\n#[derive(Debug, Default)]\npub struct OverviewState {\n    /// Whether the overview is currently open.\n    pub is_open: bool,\n}\n\n/// The config state communicated over the event stream.\n#[derive(Debug, Default)]\npub struct ConfigState {\n    /// Whether the last config load attempt had failed.\n    pub failed: bool,\n}\n\n/// The casts state communicated over the event stream.\n#[derive(Debug, Default)]\npub struct CastsState {\n    /// Map from a stream id to the screencast.\n    pub casts: HashMap<u64, Cast>,\n}\n\nimpl EventStreamStatePart for EventStreamState {\n    fn replicate(&self) -> Vec<Event> {\n        let mut events = Vec::new();\n        events.extend(self.workspaces.replicate());\n        events.extend(self.windows.replicate());\n        events.extend(self.keyboard_layouts.replicate());\n        events.extend(self.overview.replicate());\n        events.extend(self.config.replicate());\n        events.extend(self.casts.replicate());\n        events\n    }\n\n    fn apply(&mut self, event: Event) -> Option<Event> {\n        let event = self.workspaces.apply(event)?;\n        let event = self.windows.apply(event)?;\n        let event = self.keyboard_layouts.apply(event)?;\n        let event = self.overview.apply(event)?;\n        let event = self.config.apply(event)?;\n        let event = self.casts.apply(event)?;\n        Some(event)\n    }\n}\n\nimpl EventStreamStatePart for WorkspacesState {\n    fn replicate(&self) -> Vec<Event> {\n        let workspaces = self.workspaces.values().cloned().collect();\n        vec![Event::WorkspacesChanged { workspaces }]\n    }\n\n    fn apply(&mut self, event: Event) -> Option<Event> {\n        match event {\n            Event::WorkspacesChanged { workspaces } => {\n                self.workspaces = workspaces.into_iter().map(|ws| (ws.id, ws)).collect();\n            }\n            Event::WorkspaceUrgencyChanged { id, urgent } => {\n                for ws in self.workspaces.values_mut() {\n                    if ws.id == id {\n                        ws.is_urgent = urgent;\n                    }\n                }\n            }\n            Event::WorkspaceActivated { id, focused } => {\n                let ws = self.workspaces.get(&id);\n                let ws = ws.expect(\"activated workspace was missing from the map\");\n                let output = ws.output.clone();\n\n                for ws in self.workspaces.values_mut() {\n                    let got_activated = ws.id == id;\n                    if ws.output == output {\n                        ws.is_active = got_activated;\n                    }\n\n                    if focused {\n                        ws.is_focused = got_activated;\n                    }\n                }\n            }\n            Event::WorkspaceActiveWindowChanged {\n                workspace_id,\n                active_window_id,\n            } => {\n                let ws = self.workspaces.get_mut(&workspace_id);\n                let ws = ws.expect(\"changed workspace was missing from the map\");\n                ws.active_window_id = active_window_id;\n            }\n            event => return Some(event),\n        }\n        None\n    }\n}\n\nimpl EventStreamStatePart for WindowsState {\n    fn replicate(&self) -> Vec<Event> {\n        let windows = self.windows.values().cloned().collect();\n        vec![Event::WindowsChanged { windows }]\n    }\n\n    fn apply(&mut self, event: Event) -> Option<Event> {\n        match event {\n            Event::WindowsChanged { windows } => {\n                self.windows = windows.into_iter().map(|win| (win.id, win)).collect();\n            }\n            Event::WindowOpenedOrChanged { window } => {\n                let (id, is_focused) = match self.windows.entry(window.id) {\n                    Entry::Occupied(mut entry) => {\n                        let entry = entry.get_mut();\n                        *entry = window;\n                        (entry.id, entry.is_focused)\n                    }\n                    Entry::Vacant(entry) => {\n                        let entry = entry.insert(window);\n                        (entry.id, entry.is_focused)\n                    }\n                };\n\n                if is_focused {\n                    for win in self.windows.values_mut() {\n                        if win.id != id {\n                            win.is_focused = false;\n                        }\n                    }\n                }\n            }\n            Event::WindowClosed { id } => {\n                let win = self.windows.remove(&id);\n                win.expect(\"closed window was missing from the map\");\n            }\n            Event::WindowFocusChanged { id } => {\n                for win in self.windows.values_mut() {\n                    win.is_focused = Some(win.id) == id;\n                }\n            }\n            Event::WindowFocusTimestampChanged {\n                id,\n                focus_timestamp,\n            } => {\n                for win in self.windows.values_mut() {\n                    if win.id == id {\n                        win.focus_timestamp = focus_timestamp;\n                        break;\n                    }\n                }\n            }\n            Event::WindowUrgencyChanged { id, urgent } => {\n                for win in self.windows.values_mut() {\n                    if win.id == id {\n                        win.is_urgent = urgent;\n                        break;\n                    }\n                }\n            }\n            Event::WindowLayoutsChanged { changes } => {\n                for (id, update) in changes {\n                    let win = self.windows.get_mut(&id);\n                    let win = win.expect(\"changed window was missing from the map\");\n                    win.layout = update;\n                }\n            }\n            event => return Some(event),\n        }\n        None\n    }\n}\n\nimpl EventStreamStatePart for KeyboardLayoutsState {\n    fn replicate(&self) -> Vec<Event> {\n        if let Some(keyboard_layouts) = self.keyboard_layouts.clone() {\n            vec![Event::KeyboardLayoutsChanged { keyboard_layouts }]\n        } else {\n            vec![]\n        }\n    }\n\n    fn apply(&mut self, event: Event) -> Option<Event> {\n        match event {\n            Event::KeyboardLayoutsChanged { keyboard_layouts } => {\n                self.keyboard_layouts = Some(keyboard_layouts);\n            }\n            Event::KeyboardLayoutSwitched { idx } => {\n                let kb = self.keyboard_layouts.as_mut();\n                let kb = kb.expect(\"keyboard layouts must be set before a layout can be switched\");\n                kb.current_idx = idx;\n            }\n            event => return Some(event),\n        }\n        None\n    }\n}\n\nimpl EventStreamStatePart for OverviewState {\n    fn replicate(&self) -> Vec<Event> {\n        vec![Event::OverviewOpenedOrClosed {\n            is_open: self.is_open,\n        }]\n    }\n\n    fn apply(&mut self, event: Event) -> Option<Event> {\n        match event {\n            Event::OverviewOpenedOrClosed { is_open } => {\n                self.is_open = is_open;\n            }\n            event => return Some(event),\n        }\n        None\n    }\n}\n\nimpl EventStreamStatePart for ConfigState {\n    fn replicate(&self) -> Vec<Event> {\n        vec![Event::ConfigLoaded {\n            failed: self.failed,\n        }]\n    }\n\n    fn apply(&mut self, event: Event) -> Option<Event> {\n        match event {\n            Event::ConfigLoaded { failed } => {\n                self.failed = failed;\n            }\n            event => return Some(event),\n        }\n        None\n    }\n}\n\nimpl EventStreamStatePart for CastsState {\n    fn replicate(&self) -> Vec<Event> {\n        let casts = self.casts.values().cloned().collect();\n        vec![Event::CastsChanged { casts }]\n    }\n\n    fn apply(&mut self, event: Event) -> Option<Event> {\n        match event {\n            Event::CastsChanged { casts } => {\n                self.casts = casts.into_iter().map(|c| (c.stream_id, c)).collect();\n            }\n            Event::CastStartedOrChanged { cast } => {\n                self.casts.insert(cast.stream_id, cast);\n            }\n            Event::CastStopped { stream_id } => {\n                let cast = self.casts.remove(&stream_id);\n                cast.expect(\"stopped cast was missing from the map\");\n            }\n            event => return Some(event),\n        }\n        None\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/Cargo.toml",
    "content": "[package]\nname = \"niri-visual-tests\"\nversion.workspace = true\ndescription.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrepository.workspace = true\n\n[dependencies]\nadw = { version = \"0.8.1\", package = \"libadwaita\", features = [\"v1_4\"] }\nanyhow.workspace = true\ngtk = { version = \"0.10.3\", package = \"gtk4\", features = [\"v4_12\"] }\nniri = { version = \"25.11.0\", path = \"..\" }\nniri-config = { version = \"25.11.0\", path = \"../niri-config\" }\nsmithay.workspace = true\ntracing.workspace = true\ntracing-subscriber.workspace = true\n"
  },
  {
    "path": "niri-visual-tests/README.md",
    "content": "# niri-visual-tests\n\n> [!NOTE]\n>\n> This is a development-only app, you shouldn't package it.\n\nThis app contains a number of hard-coded test scenarios for visual inspection.\nIt uses the real niri layout and rendering code, but with mock windows instead of Wayland clients.\nThe idea is to go through the test scenarios and check that everything *looks* right.\n\n## Running\n\nYou will need recent GTK and libadwaita.\nThen, `cargo run`.\n"
  },
  {
    "path": "niri-visual-tests/resources/style.css",
    "content": ".anim-control-bar {\n    padding: 12px;\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_angle.rs",
    "content": "use std::f32::consts::{FRAC_PI_2, PI};\nuse std::time::Duration;\n\nuse niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{Color, CornerRadius, GradientInterpolation};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientAngle {\n    angle: f32,\n    prev_time: Duration,\n}\n\nimpl GradientAngle {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            angle: 0.,\n            prev_time: Duration::ZERO,\n        }\n    }\n}\n\nimpl TestCase for GradientAngle {\n    fn are_animations_ongoing(&self) -> bool {\n        true\n    }\n\n    fn advance_animations(&mut self, current_time: Duration) {\n        let delta = if self.prev_time.is_zero() {\n            Duration::ZERO\n        } else {\n            current_time.saturating_sub(self.prev_time)\n        };\n        self.prev_time = current_time;\n\n        self.angle += delta.as_secs_f32() * PI;\n\n        if self.angle >= PI * 2. {\n            self.angle -= PI * 2.\n        }\n    }\n\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 4, size.h / 4);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            GradientInterpolation::default(),\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            self.angle - FRAC_PI_2,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_area.rs",
    "content": "use std::f32::consts::{FRAC_PI_4, PI};\nuse std::time::Duration;\n\nuse niri::layout::focus_ring::FocusRing;\nuse niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{Color, CornerRadius, GradientInterpolation};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientArea {\n    progress: f32,\n    border: FocusRing,\n    prev_time: Duration,\n}\n\nimpl GradientArea {\n    pub fn new(_args: Args) -> Self {\n        let border = FocusRing::new(niri_config::FocusRing {\n            off: false,\n            width: 1.,\n            active_color: Color::from_rgba8_unpremul(255, 255, 255, 128),\n            inactive_color: Color::default(),\n            urgent_color: Color::default(),\n            active_gradient: None,\n            inactive_gradient: None,\n            urgent_gradient: None,\n        });\n\n        Self {\n            progress: 0.,\n            border,\n            prev_time: Duration::ZERO,\n        }\n    }\n}\n\nimpl TestCase for GradientArea {\n    fn are_animations_ongoing(&self) -> bool {\n        true\n    }\n\n    fn advance_animations(&mut self, current_time: Duration) {\n        let delta = if self.prev_time.is_zero() {\n            Duration::ZERO\n        } else {\n            current_time.saturating_sub(self.prev_time)\n        };\n        self.prev_time = current_time;\n\n        self.progress += delta.as_secs_f32() * PI;\n\n        if self.progress >= PI * 2. {\n            self.progress -= PI * 2.\n        }\n    }\n\n    fn render(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let mut rv = Vec::new();\n\n        let f = (self.progress.sin() + 1.) / 2.;\n\n        let (a, b) = (size.w / 4, size.h / 4);\n        let rect_size = Size::from((size.w - a * 2, size.h - b * 2));\n        let area = Rectangle::new(Point::from((a, b)), rect_size).to_f64();\n\n        let g_size = Size::from((\n            (size.w as f32 / 8. + size.w as f32 / 8. * 7. * f).round() as i32,\n            (size.h as f32 / 8. + size.h as f32 / 8. * 7. * f).round() as i32,\n        ));\n        let g_loc = Point::from(((size.w - g_size.w) / 2, (size.h - g_size.h) / 2)).to_f64();\n        let g_size = g_size.to_f64();\n        let mut g_area = Rectangle::new(g_loc, g_size);\n        g_area.loc -= area.loc;\n\n        self.border.update_render_elements(\n            g_size,\n            true,\n            true,\n            false,\n            Rectangle::default(),\n            CornerRadius::default(),\n            1.,\n            1.,\n        );\n        self.border\n            .render(renderer, g_loc, &mut |elem| rv.push(Box::new(elem) as _));\n\n        rv.extend(\n            [BorderRenderElement::new(\n                area.size,\n                g_area,\n                GradientInterpolation::default(),\n                Color::new_unpremul(1., 0., 0., 1.),\n                Color::new_unpremul(0., 1., 0., 1.),\n                FRAC_PI_4,\n                Rectangle::from_size(rect_size).to_f64(),\n                0.,\n                CornerRadius::default(),\n                1.,\n                1.,\n            )\n            .with_location(area.loc)]\n            .into_iter()\n            .map(|elem| Box::new(elem) as _),\n        );\n\n        rv\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_oklab.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientOklab {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientOklab {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Oklab,\n                hue_interpolation: HueInterpolation::Shorter,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientOklab {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_oklab_alpha.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientOklabAlpha {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientOklabAlpha {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Oklab,\n                hue_interpolation: Default::default(),\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientOklabAlpha {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 0.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_oklch_alpha.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientOklchAlpha {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientOklchAlpha {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Longer,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientOklchAlpha {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 0.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_oklch_decreasing.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientOklchDecreasing {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientOklchDecreasing {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Decreasing,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientOklchDecreasing {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_oklch_increasing.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientOklchIncreasing {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientOklchIncreasing {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Increasing,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientOklchIncreasing {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_oklch_longer.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientOklchLonger {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientOklchLonger {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Longer,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientOklchLonger {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_oklch_shorter.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientOklchShorter {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientOklchShorter {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Oklch,\n                hue_interpolation: HueInterpolation::Shorter,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientOklchShorter {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_srgb.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientSrgb {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientSrgb {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Srgb,\n                hue_interpolation: HueInterpolation::Shorter,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientSrgb {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_srgb_alpha.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientSrgbAlpha {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientSrgbAlpha {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::Srgb,\n                hue_interpolation: Default::default(),\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientSrgbAlpha {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 0.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_srgblinear.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientSrgbLinear {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientSrgbLinear {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::SrgbLinear,\n                hue_interpolation: HueInterpolation::Shorter,\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientSrgbLinear {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 1.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/gradient_srgblinear_alpha.rs",
    "content": "use niri::render_helpers::border::BorderRenderElement;\nuse niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\n\npub struct GradientSrgbLinearAlpha {\n    gradient_format: GradientInterpolation,\n}\n\nimpl GradientSrgbLinearAlpha {\n    pub fn new(_args: Args) -> Self {\n        Self {\n            gradient_format: GradientInterpolation {\n                color_space: GradientColorSpace::SrgbLinear,\n                hue_interpolation: Default::default(),\n            },\n        }\n    }\n}\n\nimpl TestCase for GradientSrgbLinearAlpha {\n    fn render(\n        &mut self,\n        _renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let (a, b) = (size.w / 6, size.h / 3);\n        let size = (size.w - a * 2, size.h - b * 2);\n        let area = Rectangle::new(Point::from((a, b)), Size::from(size)).to_f64();\n\n        [BorderRenderElement::new(\n            area.size,\n            Rectangle::from_size(area.size),\n            self.gradient_format,\n            Color::new_unpremul(1., 0., 0., 1.),\n            Color::new_unpremul(0., 1., 0., 0.),\n            0.,\n            Rectangle::from_size(area.size),\n            0.,\n            CornerRadius::default(),\n            1.,\n            1.,\n        )\n        .with_location(area.loc)]\n        .into_iter()\n        .map(|elem| Box::new(elem) as _)\n        .collect()\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/layout.rs",
    "content": "use std::collections::HashMap;\nuse std::time::Duration;\n\nuse niri::animation::Clock;\nuse niri::layout::{ActivateWindow, AddWindowTarget, LayoutElement as _, Options, SizingMode};\nuse niri::render_helpers::RenderTarget;\nuse niri_config::{Color, OutputName, PresetSize};\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::desktop::layer_map_for_output;\nuse smithay::output::{Mode, Output, PhysicalProperties, Subpixel};\nuse smithay::utils::{Physical, Size};\n\nuse super::{Args, TestCase};\nuse crate::test_window::TestWindow;\n\ntype DynStepFn = Box<dyn FnOnce(&mut Layout)>;\n\npub struct Layout {\n    output: Output,\n    windows: Vec<TestWindow>,\n    clock: Clock,\n    layout: niri::layout::Layout<TestWindow>,\n    start_time: Duration,\n    steps: HashMap<Duration, DynStepFn>,\n}\n\nimpl Layout {\n    pub fn new(args: Args) -> Self {\n        let Args { size, clock } = args;\n\n        let output = Output::new(\n            String::new(),\n            PhysicalProperties {\n                size: Size::from((size.w, size.h)),\n                subpixel: Subpixel::Unknown,\n                make: String::new(),\n                model: String::new(),\n                serial_number: String::new(),\n            },\n        );\n        let mode = Some(Mode {\n            size: size.to_physical(1),\n            refresh: 60000,\n        });\n        output.change_current_state(mode, None, None, None);\n        output.user_data().insert_if_missing(|| OutputName {\n            connector: String::new(),\n            make: None,\n            model: None,\n            serial: None,\n        });\n\n        let options = Options {\n            layout: niri_config::Layout {\n                focus_ring: niri_config::FocusRing {\n                    off: true,\n                    ..Default::default()\n                },\n                border: niri_config::Border {\n                    off: false,\n                    width: 4.,\n                    active_color: Color::from_rgba8_unpremul(255, 163, 72, 255),\n                    inactive_color: Color::from_rgba8_unpremul(50, 50, 50, 255),\n                    urgent_color: Color::from_rgba8_unpremul(155, 0, 0, 255),\n                    active_gradient: None,\n                    inactive_gradient: None,\n                    urgent_gradient: None,\n                },\n                ..Default::default()\n            },\n            ..Default::default()\n        };\n        let mut layout = niri::layout::Layout::with_options(clock.clone(), options);\n        layout.add_output(output.clone(), None);\n\n        let start_time = clock.now_unadjusted();\n\n        Self {\n            output,\n            windows: Vec::new(),\n            clock,\n            layout,\n            start_time,\n            steps: HashMap::new(),\n        }\n    }\n\n    pub fn open_in_between(args: Args) -> Self {\n        let mut rv = Self::new(args);\n\n        rv.add_window(TestWindow::freeform(0), Some(PresetSize::Proportion(0.3)));\n        rv.add_window(TestWindow::freeform(1), Some(PresetSize::Proportion(0.3)));\n        rv.layout.activate_window(&0);\n\n        rv.add_step(500, |l| {\n            let win = TestWindow::freeform(2);\n            l.add_window(win.clone(), Some(PresetSize::Proportion(0.3)));\n            l.layout.start_open_animation_for_window(win.id());\n        });\n\n        rv\n    }\n\n    pub fn open_multiple_quickly(args: Args) -> Self {\n        let mut rv = Self::new(args);\n\n        for delay in [100, 200, 300] {\n            rv.add_step(delay, move |l| {\n                let win = TestWindow::freeform(delay as usize);\n                l.add_window(win.clone(), Some(PresetSize::Proportion(0.3)));\n                l.layout.start_open_animation_for_window(win.id());\n            });\n        }\n\n        rv\n    }\n\n    pub fn open_multiple_quickly_big(args: Args) -> Self {\n        let mut rv = Self::new(args);\n\n        for delay in [100, 200, 300] {\n            rv.add_step(delay, move |l| {\n                let win = TestWindow::freeform(delay as usize);\n                l.add_window(win.clone(), Some(PresetSize::Proportion(0.5)));\n                l.layout.start_open_animation_for_window(win.id());\n            });\n        }\n\n        rv\n    }\n\n    pub fn open_to_the_left(args: Args) -> Self {\n        let mut rv = Self::new(args);\n\n        rv.add_window(TestWindow::freeform(0), Some(PresetSize::Proportion(0.3)));\n        rv.add_window(TestWindow::freeform(1), Some(PresetSize::Proportion(0.3)));\n\n        rv.add_step(500, |l| {\n            let win = TestWindow::freeform(2);\n            let right_of = l.windows[0].clone();\n            l.add_window_right_of(&right_of, win.clone(), Some(PresetSize::Proportion(0.3)));\n            l.layout.start_open_animation_for_window(win.id());\n        });\n\n        rv\n    }\n\n    pub fn open_to_the_left_big(args: Args) -> Self {\n        let mut rv = Self::new(args);\n\n        rv.add_window(TestWindow::freeform(0), Some(PresetSize::Proportion(0.3)));\n        rv.add_window(TestWindow::freeform(1), Some(PresetSize::Proportion(0.8)));\n\n        rv.add_step(500, |l| {\n            let win = TestWindow::freeform(2);\n            let right_of = l.windows[0].clone();\n            l.add_window_right_of(&right_of, win.clone(), Some(PresetSize::Proportion(0.5)));\n            l.layout.start_open_animation_for_window(win.id());\n        });\n\n        rv\n    }\n\n    fn add_window(&mut self, mut window: TestWindow, width: Option<PresetSize>) {\n        let ws = self.layout.active_workspace().unwrap();\n        let min_size = window.min_size();\n        let max_size = window.max_size();\n        window.request_size(\n            ws.new_window_size(width, None, false, window.rules(), (min_size, max_size)),\n            SizingMode::Normal,\n            false,\n            None,\n        );\n        window.communicate();\n\n        self.layout.add_window(\n            window.clone(),\n            AddWindowTarget::Auto,\n            width,\n            None,\n            false,\n            false,\n            ActivateWindow::default(),\n        );\n        self.windows.push(window);\n    }\n\n    fn add_window_right_of(\n        &mut self,\n        right_of: &TestWindow,\n        mut window: TestWindow,\n        width: Option<PresetSize>,\n    ) {\n        let ws = self.layout.active_workspace().unwrap();\n        let min_size = window.min_size();\n        let max_size = window.max_size();\n        window.request_size(\n            ws.new_window_size(width, None, false, window.rules(), (min_size, max_size)),\n            SizingMode::Normal,\n            false,\n            None,\n        );\n        window.communicate();\n\n        self.layout.add_window(\n            window.clone(),\n            AddWindowTarget::NextTo(right_of.id()),\n            width,\n            None,\n            false,\n            false,\n            ActivateWindow::default(),\n        );\n        self.windows.push(window);\n    }\n\n    fn add_step(&mut self, delay_ms: u64, f: impl FnOnce(&mut Self) + 'static) {\n        self.steps\n            .insert(Duration::from_millis(delay_ms), Box::new(f) as _);\n    }\n}\n\nimpl TestCase for Layout {\n    fn resize(&mut self, width: i32, height: i32) {\n        let mode = Some(Mode {\n            size: Size::from((width, height)),\n            refresh: 60000,\n        });\n        self.output.change_current_state(mode, None, None, None);\n        layer_map_for_output(&self.output).arrange();\n        self.layout.update_output_size(&self.output);\n        for win in &self.windows {\n            if win.communicate() {\n                self.layout.update_window(win.id(), None);\n            }\n        }\n    }\n\n    fn are_animations_ongoing(&self) -> bool {\n        self.layout.are_animations_ongoing(Some(&self.output)) || !self.steps.is_empty()\n    }\n\n    fn advance_animations(&mut self, _current_time: Duration) {\n        let now_unadjusted = self.clock.now_unadjusted();\n        let run = self\n            .steps\n            .keys()\n            .copied()\n            .filter(|delay| self.start_time + *delay <= now_unadjusted)\n            .collect::<Vec<_>>();\n        for delay in &run {\n            let now = self.start_time + *delay;\n            self.clock.set_unadjusted(now);\n            self.layout.advance_animations();\n\n            let f = self.steps.remove(delay).unwrap();\n            f(self);\n        }\n\n        self.clock.set_unadjusted(now_unadjusted);\n        self.layout.advance_animations();\n    }\n\n    fn render(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        _size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        self.layout.update_render_elements(Some(&self.output));\n\n        let mut rv = Vec::new();\n        self.layout\n            .monitor_for_output(&self.output)\n            .unwrap()\n            .render_workspaces(renderer, RenderTarget::Output, true, &mut |elem| {\n                rv.push(Box::new(elem) as _)\n            });\n        rv\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/mod.rs",
    "content": "use std::time::Duration;\n\nuse niri::animation::Clock;\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Logical, Physical, Size};\n\npub mod gradient_angle;\npub mod gradient_area;\npub mod gradient_oklab;\npub mod gradient_oklab_alpha;\npub mod gradient_oklch_alpha;\npub mod gradient_oklch_decreasing;\npub mod gradient_oklch_increasing;\npub mod gradient_oklch_longer;\npub mod gradient_oklch_shorter;\npub mod gradient_srgb;\npub mod gradient_srgb_alpha;\npub mod gradient_srgblinear;\npub mod gradient_srgblinear_alpha;\npub mod layout;\npub mod tile;\npub mod window;\n\npub struct Args {\n    pub size: Size<i32, Logical>,\n    pub clock: Clock,\n}\n\npub trait TestCase {\n    fn resize(&mut self, _width: i32, _height: i32) {}\n    fn are_animations_ongoing(&self) -> bool {\n        false\n    }\n    fn advance_animations(&mut self, _current_time: Duration) {}\n    fn render(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>>;\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/tile.rs",
    "content": "use std::rc::Rc;\nuse std::time::Duration;\n\nuse niri::layout::Options;\nuse niri::render_helpers::RenderTarget;\nuse niri_config::Color;\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Rectangle, Size};\n\nuse super::{Args, TestCase};\nuse crate::test_window::TestWindow;\n\npub struct Tile {\n    window: TestWindow,\n    tile: niri::layout::tile::Tile<TestWindow>,\n}\n\nimpl Tile {\n    pub fn freeform(args: Args) -> Self {\n        let window = TestWindow::freeform(0);\n        Self::with_window(args, window)\n    }\n\n    pub fn fixed_size(args: Args) -> Self {\n        let window = TestWindow::fixed_size(0);\n        Self::with_window(args, window)\n    }\n\n    pub fn fixed_size_with_csd_shadow(args: Args) -> Self {\n        let window = TestWindow::fixed_size(0);\n        window.set_csd_shadow_width(64);\n        Self::with_window(args, window)\n    }\n\n    pub fn freeform_open(args: Args) -> Self {\n        let mut rv = Self::freeform(args);\n        rv.window.set_color([0.1, 0.1, 0.1, 1.]);\n        rv.tile.start_open_animation();\n        rv\n    }\n\n    pub fn fixed_size_open(args: Args) -> Self {\n        let mut rv = Self::fixed_size(args);\n        rv.window.set_color([0.1, 0.1, 0.1, 1.]);\n        rv.tile.start_open_animation();\n        rv\n    }\n\n    pub fn fixed_size_with_csd_shadow_open(args: Args) -> Self {\n        let mut rv = Self::fixed_size_with_csd_shadow(args);\n        rv.window.set_color([0.1, 0.1, 0.1, 1.]);\n        rv.tile.start_open_animation();\n        rv\n    }\n\n    pub fn with_window(args: Args, window: TestWindow) -> Self {\n        let Args { size, clock } = args;\n\n        let options = Options {\n            layout: niri_config::Layout {\n                focus_ring: niri_config::FocusRing {\n                    off: true,\n                    ..Default::default()\n                },\n                border: niri_config::Border {\n                    off: false,\n                    width: 32.,\n                    active_color: Color::from_rgba8_unpremul(255, 163, 72, 255),\n                    ..Default::default()\n                },\n                ..Default::default()\n            },\n            ..Default::default()\n        };\n\n        let mut tile = niri::layout::tile::Tile::new(\n            window.clone(),\n            size.to_f64(),\n            1.,\n            clock,\n            Rc::new(options),\n        );\n\n        tile.request_tile_size(size.to_f64(), false, None);\n        window.communicate();\n\n        Self { window, tile }\n    }\n}\n\nimpl TestCase for Tile {\n    fn resize(&mut self, width: i32, height: i32) {\n        let size = Size::from((width, height)).to_f64();\n        self.tile\n            .update_config(size, 1., self.tile.options().clone());\n        self.tile.request_tile_size(size, false, None);\n        self.window.communicate();\n    }\n\n    fn are_animations_ongoing(&self) -> bool {\n        self.tile.are_animations_ongoing()\n    }\n\n    fn advance_animations(&mut self, _current_time: Duration) {\n        self.tile.advance_animations();\n    }\n\n    fn render(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let size = size.to_f64();\n        let tile_size = self.tile.tile_size().to_physical(1.);\n        let location = Point::from((size.w - tile_size.w, size.h - tile_size.h)).downscale(2.);\n\n        self.tile.update_render_elements(\n            true,\n            Rectangle::new(Point::from((-location.x, -location.y)), size.to_logical(1.)),\n        );\n\n        let mut rv = Vec::new();\n        self.tile.render(\n            renderer,\n            location,\n            true,\n            RenderTarget::Output,\n            &mut |elem| rv.push(Box::new(elem) as _),\n        );\n        rv\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/cases/window.rs",
    "content": "use niri::layout::{LayoutElement, SizingMode};\nuse niri::render_helpers::RenderTarget;\nuse smithay::backend::renderer::element::RenderElement;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Physical, Point, Scale, Size};\n\nuse super::{Args, TestCase};\nuse crate::test_window::TestWindow;\n\npub struct Window {\n    window: TestWindow,\n}\n\nimpl Window {\n    pub fn freeform(args: Args) -> Self {\n        let mut window = TestWindow::freeform(0);\n        window.request_size(args.size, SizingMode::Normal, false, None);\n        window.communicate();\n        Self { window }\n    }\n\n    pub fn fixed_size(args: Args) -> Self {\n        let mut window = TestWindow::fixed_size(0);\n        window.request_size(args.size, SizingMode::Normal, false, None);\n        window.communicate();\n        Self { window }\n    }\n\n    pub fn fixed_size_with_csd_shadow(args: Args) -> Self {\n        let mut window = TestWindow::fixed_size(0);\n        window.set_csd_shadow_width(64);\n        window.request_size(args.size, SizingMode::Normal, false, None);\n        window.communicate();\n        Self { window }\n    }\n}\n\nimpl TestCase for Window {\n    fn resize(&mut self, width: i32, height: i32) {\n        self.window\n            .request_size(Size::from((width, height)), SizingMode::Normal, false, None);\n        self.window.communicate();\n    }\n\n    fn render(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        size: Size<i32, Physical>,\n    ) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {\n        let win_size = self.window.size().to_physical(1);\n        let location = Point::from((size.w - win_size.w, size.h - win_size.h))\n            .to_f64()\n            .downscale(2.);\n\n        let mut rv = Vec::new();\n        self.window.render_normal(\n            renderer,\n            location,\n            Scale::from(1.),\n            1.,\n            RenderTarget::Output,\n            &mut |elem| rv.push(Box::new(elem) as _),\n        );\n        rv\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/main.rs",
    "content": "#[macro_use]\nextern crate tracing;\n\nuse std::env;\n\nuse adw::prelude::{AdwApplicationWindowExt, NavigationPageExt};\nuse cases::Args;\nuse gtk::prelude::{ApplicationExt, ApplicationExtManual, BoxExt, GtkWindowExt, WidgetExt};\nuse gtk::{gdk, gio, glib};\nuse smithay_view::SmithayView;\nuse tracing_subscriber::EnvFilter;\n\nuse crate::cases::gradient_angle::GradientAngle;\nuse crate::cases::gradient_area::GradientArea;\nuse crate::cases::gradient_oklab::GradientOklab;\nuse crate::cases::gradient_oklab_alpha::GradientOklabAlpha;\nuse crate::cases::gradient_oklch_alpha::GradientOklchAlpha;\nuse crate::cases::gradient_oklch_decreasing::GradientOklchDecreasing;\nuse crate::cases::gradient_oklch_increasing::GradientOklchIncreasing;\nuse crate::cases::gradient_oklch_longer::GradientOklchLonger;\nuse crate::cases::gradient_oklch_shorter::GradientOklchShorter;\nuse crate::cases::gradient_srgb::GradientSrgb;\nuse crate::cases::gradient_srgb_alpha::GradientSrgbAlpha;\nuse crate::cases::gradient_srgblinear::GradientSrgbLinear;\nuse crate::cases::gradient_srgblinear_alpha::GradientSrgbLinearAlpha;\nuse crate::cases::layout::Layout;\nuse crate::cases::tile::Tile;\nuse crate::cases::window::Window;\nuse crate::cases::TestCase;\n\nmod cases;\nmod smithay_view;\nmod test_window;\n\nfn main() -> glib::ExitCode {\n    let directives =\n        env::var(\"RUST_LOG\").unwrap_or_else(|_| \"niri-visual-tests=debug,niri=debug\".to_owned());\n    let env_filter = EnvFilter::builder().parse_lossy(directives);\n    tracing_subscriber::fmt()\n        .compact()\n        .with_env_filter(env_filter)\n        .init();\n\n    let app = adw::Application::new(None::<&str>, gio::ApplicationFlags::NON_UNIQUE);\n    app.connect_startup(on_startup);\n    app.connect_activate(build_ui);\n    app.run()\n}\n\nfn on_startup(_app: &adw::Application) {\n    // Load our CSS.\n    let provider = gtk::CssProvider::new();\n    provider.load_from_string(include_str!(\"../resources/style.css\"));\n    if let Some(display) = gdk::Display::default() {\n        gtk::style_context_add_provider_for_display(\n            &display,\n            &provider,\n            gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,\n        );\n    }\n}\n\nfn build_ui(app: &adw::Application) {\n    let stack = gtk::Stack::new();\n    let anim_adjustment = gtk::Adjustment::new(1., 0., 10., 0.1, 0.5, 0.);\n\n    struct S {\n        stack: gtk::Stack,\n        anim_adjustment: gtk::Adjustment,\n    }\n\n    impl S {\n        fn add<T: TestCase + 'static>(&self, make: impl Fn(Args) -> T + 'static, title: &str) {\n            let view = SmithayView::new(make, &self.anim_adjustment);\n            self.stack.add_titled(&view, None, title);\n        }\n    }\n\n    let s = S {\n        stack: stack.clone(),\n        anim_adjustment: anim_adjustment.clone(),\n    };\n\n    s.add(Window::freeform, \"Freeform Window\");\n    s.add(Window::fixed_size, \"Fixed Size Window\");\n    s.add(\n        Window::fixed_size_with_csd_shadow,\n        \"Fixed Size Window - CSD Shadow\",\n    );\n\n    s.add(Tile::freeform, \"Freeform Tile\");\n    s.add(Tile::fixed_size, \"Fixed Size Tile\");\n    s.add(\n        Tile::fixed_size_with_csd_shadow,\n        \"Fixed Size Tile - CSD Shadow\",\n    );\n    s.add(Tile::freeform_open, \"Freeform Tile - Open\");\n    s.add(Tile::fixed_size_open, \"Fixed Size Tile - Open\");\n    s.add(\n        Tile::fixed_size_with_csd_shadow_open,\n        \"Fixed Size Tile - CSD Shadow - Open\",\n    );\n\n    s.add(Layout::open_in_between, \"Layout - Open In-Between\");\n    s.add(\n        Layout::open_multiple_quickly,\n        \"Layout - Open Multiple Quickly\",\n    );\n    s.add(\n        Layout::open_multiple_quickly_big,\n        \"Layout - Open Multiple Quickly - Big\",\n    );\n    s.add(Layout::open_to_the_left, \"Layout - Open To The Left\");\n    s.add(\n        Layout::open_to_the_left_big,\n        \"Layout - Open To The Left - Big\",\n    );\n\n    s.add(GradientAngle::new, \"Gradient - Angle\");\n    s.add(GradientArea::new, \"Gradient - Area\");\n    s.add(GradientSrgb::new, \"Gradient - Srgb\");\n    s.add(GradientSrgbLinear::new, \"Gradient - SrgbLinear\");\n    s.add(GradientOklab::new, \"Gradient - Oklab\");\n    s.add(GradientOklchShorter::new, \"Gradient - Oklch Shorter\");\n    s.add(GradientOklchLonger::new, \"Gradient - Oklch Longer\");\n    s.add(GradientOklchIncreasing::new, \"Gradient - Oklch Increasing\");\n    s.add(GradientOklchDecreasing::new, \"Gradient - Oklch Decreasing\");\n    s.add(GradientSrgbAlpha::new, \"Gradient - Srgb Alpha\");\n    s.add(GradientSrgbLinearAlpha::new, \"Gradient - SrgbLinear Alpha\");\n    s.add(GradientOklabAlpha::new, \"Gradient - Oklab Alpha\");\n    s.add(GradientOklchAlpha::new, \"Gradient - Oklch Alpha\");\n\n    let content_headerbar = adw::HeaderBar::new();\n\n    let anim_scale = gtk::Scale::new(gtk::Orientation::Horizontal, Some(&anim_adjustment));\n    anim_scale.set_hexpand(true);\n\n    let anim_control_bar = gtk::Box::new(gtk::Orientation::Horizontal, 6);\n    anim_control_bar.add_css_class(\"anim-control-bar\");\n    anim_control_bar.append(&gtk::Label::new(Some(\"Slowdown\")));\n    anim_control_bar.append(&anim_scale);\n\n    let content_view = adw::ToolbarView::new();\n    content_view.set_top_bar_style(adw::ToolbarStyle::RaisedBorder);\n    content_view.set_bottom_bar_style(adw::ToolbarStyle::RaisedBorder);\n    content_view.add_top_bar(&content_headerbar);\n    content_view.add_bottom_bar(&anim_control_bar);\n    content_view.set_content(Some(&stack));\n    let content = adw::NavigationPage::new(\n        &content_view,\n        stack\n            .page(&stack.visible_child().unwrap())\n            .title()\n            .as_deref()\n            .unwrap(),\n    );\n\n    let sidebar_header = adw::HeaderBar::new();\n    let stack_sidebar = gtk::StackSidebar::new();\n    stack_sidebar.set_stack(&stack);\n    let sidebar_view = adw::ToolbarView::new();\n    sidebar_view.add_top_bar(&sidebar_header);\n    sidebar_view.set_content(Some(&stack_sidebar));\n    let sidebar = adw::NavigationPage::new(&sidebar_view, \"Tests\");\n\n    let split_view = adw::NavigationSplitView::new();\n    split_view.set_content(Some(&content));\n    split_view.set_sidebar(Some(&sidebar));\n\n    stack.connect_visible_child_notify(move |stack| {\n        content.set_title(\n            stack\n                .visible_child()\n                .and_then(|c| stack.page(&c).title())\n                .as_deref()\n                .unwrap_or_default(),\n        )\n    });\n\n    let window = adw::ApplicationWindow::new(app);\n    window.set_title(Some(\"niri visual tests\"));\n    window.set_content(Some(&split_view));\n    window.present();\n}\n"
  },
  {
    "path": "niri-visual-tests/src/smithay_view.rs",
    "content": "use gtk::glib;\nuse gtk::prelude::*;\nuse gtk::subclass::prelude::*;\nuse smithay::utils::Size;\n\nuse crate::cases::{Args, TestCase};\n\nmod imp {\n    use std::cell::{Cell, OnceCell, RefCell};\n    use std::ptr::null;\n    use std::time::Duration;\n\n    use anyhow::{ensure, Context};\n    use gtk::gdk;\n    use gtk::prelude::*;\n    use niri::animation::Clock;\n    use niri::render_helpers::{resources, shaders};\n    use smithay::backend::egl::ffi::egl;\n    use smithay::backend::egl::EGLContext;\n    use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\n    use smithay::backend::renderer::{Bind, Color32F, Frame, Offscreen, Renderer};\n    use smithay::reexports::gbm::Format as Fourcc;\n    use smithay::utils::{Physical, Rectangle, Scale, Transform};\n\n    use super::*;\n\n    type DynMakeTestCase = Box<dyn Fn(Args) -> Box<dyn TestCase>>;\n\n    struct RendererData {\n        renderer: GlesRenderer,\n        dummy_texture: GlesTexture,\n    }\n\n    #[derive(Default)]\n    pub struct SmithayView {\n        gl_area: gtk::GLArea,\n        size: Cell<(i32, i32)>,\n        renderer: RefCell<Option<Result<RendererData, ()>>>,\n        pub make_test_case: OnceCell<DynMakeTestCase>,\n        test_case: RefCell<Option<Box<dyn TestCase>>>,\n        pub clock: RefCell<Clock>,\n    }\n\n    #[glib::object_subclass]\n    impl ObjectSubclass for SmithayView {\n        const NAME: &'static str = \"NiriSmithayView\";\n        type Type = super::SmithayView;\n        type ParentType = gtk::Widget;\n\n        fn class_init(klass: &mut Self::Class) {\n            klass.set_layout_manager_type::<gtk::BinLayout>();\n        }\n    }\n\n    impl ObjectImpl for SmithayView {\n        fn constructed(&self) {\n            let obj = self.obj();\n\n            self.parent_constructed();\n\n            self.gl_area.set_allowed_apis(gdk::GLAPI::GLES);\n            self.gl_area.set_parent(&*obj);\n\n            self.gl_area.connect_resize({\n                let imp = self.downgrade();\n                move |_, width, height| {\n                    if let Some(imp) = imp.upgrade() {\n                        imp.resize(width, height);\n                    }\n                }\n            });\n\n            self.gl_area.connect_render({\n                let imp = self.downgrade();\n                move |_, gl_context| {\n                    if let Some(imp) = imp.upgrade() {\n                        if let Err(err) = imp.render(gl_context) {\n                            warn!(\"error rendering: {err:?}\");\n                        }\n                    }\n                    glib::Propagation::Stop\n                }\n            });\n\n            obj.add_tick_callback(|obj, _frame_clock| {\n                let imp = obj.imp();\n\n                if let Some(case) = &mut *imp.test_case.borrow_mut() {\n                    if case.are_animations_ongoing() {\n                        imp.gl_area.queue_draw();\n                    }\n                }\n\n                glib::ControlFlow::Continue\n            });\n        }\n\n        fn dispose(&self) {\n            self.gl_area.unparent();\n        }\n    }\n\n    impl WidgetImpl for SmithayView {\n        fn unmap(&self) {\n            self.test_case.replace(None);\n            self.parent_unmap();\n        }\n\n        fn unrealize(&self) {\n            self.renderer.replace(None);\n            self.parent_unrealize();\n        }\n    }\n\n    impl SmithayView {\n        fn resize(&self, width: i32, height: i32) {\n            self.size.set((width, height));\n\n            if let Some(case) = &mut *self.test_case.borrow_mut() {\n                case.resize(width, height);\n            }\n        }\n\n        fn render(&self, _gl_context: &gdk::GLContext) -> anyhow::Result<()> {\n            // Set up the Smithay renderer.\n            let mut renderer = self.renderer.borrow_mut();\n            let renderer = renderer.get_or_insert_with(|| {\n                unsafe { create_renderer() }\n                    .map_err(|err| warn!(\"error creating a Smithay renderer: {err:?}\"))\n            });\n            let Ok(renderer) = renderer else {\n                return Ok(());\n            };\n            let RendererData {\n                renderer,\n                dummy_texture,\n            } = renderer;\n\n            let size = self.size.get();\n\n            let frame_clock = self.obj().frame_clock().unwrap();\n            let time = Duration::from_micros(frame_clock.frame_time() as u64);\n            self.clock.borrow_mut().set_unadjusted(time);\n\n            // Create the test case if missing.\n            let mut case = self.test_case.borrow_mut();\n            let case = case.get_or_insert_with(|| {\n                let make = self.make_test_case.get().unwrap();\n                let args = Args {\n                    size: Size::from(size),\n                    clock: self.clock.borrow().clone(),\n                };\n                make(args)\n            });\n\n            case.advance_animations(self.clock.borrow_mut().now());\n\n            let rect: Rectangle<i32, Physical> = Rectangle::from_size(Size::from(size));\n\n            // Fetch GtkGLArea's framebuffer binding.\n            let mut framebuffer = 0;\n            renderer\n                .with_context(|gl| unsafe {\n                    gl.GetIntegerv(\n                        smithay::backend::renderer::gles::ffi::FRAMEBUFFER_BINDING,\n                        &mut framebuffer,\n                    );\n                })\n                .context(\"error running closure in GL context\")?;\n            ensure!(framebuffer != 0, \"error getting the framebuffer\");\n\n            // This call will already change the framebuffer binding (offscreen elements will bind\n            // intermediate textures during rendering).\n            let elements = case.render(renderer, Size::from(size));\n\n            // HACK: there's currently no way to \"just\" render into an externally bound framebuffer\n            // (like we have in this case). The render() call requires a valid target. So what\n            // we'll do is use a dummy texture as a target, then swap the framebuffer binding right\n            // before rendering.\n            let mut dummy_target = renderer\n                .bind(dummy_texture)\n                .context(\"error binding dummy texture\")?;\n\n            let mut frame = renderer\n                .render(&mut dummy_target, rect.size, Transform::Normal)\n                .context(\"error creating frame\")?;\n\n            // Now that render() bound the dummy texture, change the binding underneath it back to\n            // GtkGLArea's framebuffer, to render there instead.\n            frame\n                .with_context(|gl| unsafe {\n                    gl.BindFramebuffer(\n                        smithay::backend::renderer::gles::ffi::FRAMEBUFFER,\n                        framebuffer as u32,\n                    );\n                })\n                .context(\"error running closure in GL context\")?;\n\n            frame\n                .clear(Color32F::from([0.3, 0.3, 0.3, 1.]), &[rect])\n                .context(\"error clearing\")?;\n\n            for element in elements.iter().rev() {\n                let src = element.src();\n                let dst = element.geometry(Scale::from(1.));\n\n                if let Some(mut damage) = rect.intersection(dst) {\n                    damage.loc -= dst.loc;\n                    element\n                        .draw(&mut frame, src, dst, &[damage], &[])\n                        .context(\"error drawing element\")?;\n                }\n            }\n\n            Ok(())\n        }\n    }\n\n    unsafe fn create_renderer() -> anyhow::Result<RendererData> {\n        smithay::backend::egl::ffi::make_sure_egl_is_loaded()\n            .context(\"error loading EGL symbols in Smithay\")?;\n\n        let egl_display = egl::GetCurrentDisplay();\n        ensure!(egl_display != egl::NO_DISPLAY, \"no current EGL display\");\n\n        let egl_context = egl::GetCurrentContext();\n        ensure!(egl_context != egl::NO_CONTEXT, \"no current EGL context\");\n\n        // There's no config ID on the EGL context and there's no current EGL surface, but we don't\n        // really use it anyway so just get some random one.\n        let mut egl_config_id = null();\n        let mut num_configs = 0;\n        let res = egl::GetConfigs(egl_display, &mut egl_config_id, 1, &mut num_configs);\n        ensure!(res == egl::TRUE, \"error choosing EGL config\");\n        ensure!(num_configs != 0, \"no EGL config\");\n\n        let egl_context = EGLContext::from_raw(egl_display, egl_config_id as *const _, egl_context)\n            .context(\"error creating EGL context\")?;\n\n        let mut renderer = GlesRenderer::new(egl_context).context(\"error creating GlesRenderer\")?;\n\n        let dummy_texture = renderer\n            .create_buffer(Fourcc::Abgr8888, Size::from((1, 1)))\n            .context(\"error creating dummy texture\")?;\n\n        resources::init(&mut renderer);\n        shaders::init(&mut renderer);\n\n        Ok(RendererData {\n            renderer,\n            dummy_texture,\n        })\n    }\n}\n\nglib::wrapper! {\n    pub struct SmithayView(ObjectSubclass<imp::SmithayView>)\n        @extends gtk::Widget,\n        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;\n}\n\nimpl SmithayView {\n    pub fn new<T: TestCase + 'static>(\n        make_test_case: impl Fn(Args) -> T + 'static,\n        anim_adjustment: &gtk::Adjustment,\n    ) -> Self {\n        let obj: Self = glib::Object::builder().build();\n\n        let make = move |args| Box::new(make_test_case(args)) as Box<dyn TestCase>;\n        let make_test_case = Box::new(make) as _;\n        let _ = obj.imp().make_test_case.set(make_test_case);\n\n        anim_adjustment.connect_value_changed({\n            let obj = obj.downgrade();\n            move |adj| {\n                if let Some(obj) = obj.upgrade() {\n                    let mut clock = obj.imp().clock.borrow_mut();\n                    let instantly = adj.value() == 0.0;\n                    let rate = if instantly {\n                        1.0\n                    } else {\n                        1.0 / adj.value().max(0.001)\n                    };\n                    clock.set_rate(rate);\n                    clock.set_complete_instantly(instantly);\n                }\n            }\n        });\n\n        obj\n    }\n}\n"
  },
  {
    "path": "niri-visual-tests/src/test_window.rs",
    "content": "use std::cell::RefCell;\nuse std::cmp::{max, min};\nuse std::rc::Rc;\n\nuse niri::layout::{\n    ConfigureIntent, InteractiveResizeData, LayoutElement, LayoutElementRenderElement,\n    LayoutElementRenderSnapshot, SizingMode,\n};\nuse niri::render_helpers::offscreen::OffscreenData;\nuse niri::render_helpers::renderer::NiriRenderer;\nuse niri::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse niri::render_helpers::RenderTarget;\nuse niri::utils::transaction::Transaction;\nuse niri::window::ResolvedWindowRules;\nuse smithay::backend::renderer::element::Kind;\nuse smithay::output::{self, Output};\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::utils::{Logical, Point, Scale, Serial, Size, Transform};\n\n#[derive(Debug)]\nstruct TestWindowInner {\n    size: Size<i32, Logical>,\n    requested_size: Option<Size<i32, Logical>>,\n    min_size: Size<i32, Logical>,\n    max_size: Size<i32, Logical>,\n    buffer: SolidColorBuffer,\n    pending_sizing_mode: SizingMode,\n    csd_shadow_width: i32,\n    csd_shadow_buffer: SolidColorBuffer,\n}\n\n#[derive(Debug, Clone)]\npub struct TestWindow {\n    id: usize,\n    inner: Rc<RefCell<TestWindowInner>>,\n    rules: ResolvedWindowRules,\n}\n\nimpl TestWindow {\n    pub fn freeform(id: usize) -> Self {\n        let size = Size::from((100, 200));\n        let min_size = Size::from((0, 0));\n        let max_size = Size::from((0, 0));\n        let buffer = SolidColorBuffer::new(size.to_f64(), [0.15, 0.64, 0.41, 1.]);\n\n        Self {\n            id,\n            inner: Rc::new(RefCell::new(TestWindowInner {\n                size,\n                requested_size: None,\n                min_size,\n                max_size,\n                buffer,\n                pending_sizing_mode: SizingMode::Normal,\n                csd_shadow_width: 0,\n                csd_shadow_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 0.3]),\n            })),\n            rules: ResolvedWindowRules::default(),\n        }\n    }\n\n    pub fn fixed_size(id: usize) -> Self {\n        let rv = Self::freeform(id);\n        rv.set_min_size((200, 400).into());\n        rv.set_max_size((200, 400).into());\n        rv.set_color([0.88, 0.11, 0.14, 1.]);\n        rv.communicate();\n        rv\n    }\n\n    pub fn set_min_size(&self, size: Size<i32, Logical>) {\n        self.inner.borrow_mut().min_size = size;\n    }\n\n    pub fn set_max_size(&self, size: Size<i32, Logical>) {\n        self.inner.borrow_mut().max_size = size;\n    }\n\n    pub fn set_color(&self, color: [f32; 4]) {\n        self.inner.borrow_mut().buffer.set_color(color);\n    }\n\n    pub fn set_csd_shadow_width(&self, width: i32) {\n        self.inner.borrow_mut().csd_shadow_width = width;\n    }\n\n    pub fn communicate(&self) -> bool {\n        let mut rv = false;\n        let mut inner = self.inner.borrow_mut();\n\n        let mut new_size = inner.size;\n\n        if let Some(size) = inner.requested_size {\n            assert!(size.w >= 0);\n            assert!(size.h >= 0);\n\n            if size.w != 0 {\n                new_size.w = size.w;\n            }\n            if size.h != 0 {\n                new_size.h = size.h;\n            }\n        }\n\n        if inner.max_size.w > 0 {\n            new_size.w = min(new_size.w, inner.max_size.w);\n        }\n        if inner.max_size.h > 0 {\n            new_size.h = min(new_size.h, inner.max_size.h);\n        }\n        if inner.min_size.w > 0 {\n            new_size.w = max(new_size.w, inner.min_size.w);\n        }\n        if inner.min_size.h > 0 {\n            new_size.h = max(new_size.h, inner.min_size.h);\n        }\n\n        if inner.size != new_size {\n            inner.size = new_size;\n            inner.buffer.resize(new_size.to_f64());\n            rv = true;\n        }\n\n        let mut csd_shadow_size = new_size;\n        csd_shadow_size.w += inner.csd_shadow_width * 2;\n        csd_shadow_size.h += inner.csd_shadow_width * 2;\n        inner.csd_shadow_buffer.resize(csd_shadow_size.to_f64());\n\n        rv\n    }\n}\n\nimpl LayoutElement for TestWindow {\n    type Id = usize;\n\n    fn id(&self) -> &Self::Id {\n        &self.id\n    }\n\n    fn size(&self) -> Size<i32, Logical> {\n        self.inner.borrow().size\n    }\n\n    fn buf_loc(&self) -> Point<i32, Logical> {\n        (0, 0).into()\n    }\n\n    fn is_in_input_region(&self, _point: Point<f64, Logical>) -> bool {\n        false\n    }\n\n    fn render_normal<R: NiriRenderer>(\n        &self,\n        _renderer: &mut R,\n        location: Point<f64, Logical>,\n        _scale: Scale<f64>,\n        alpha: f32,\n        _target: RenderTarget,\n        push: &mut dyn FnMut(LayoutElementRenderElement<R>),\n    ) {\n        let inner = self.inner.borrow();\n\n        push(\n            SolidColorRenderElement::from_buffer(&inner.buffer, location, alpha, Kind::Unspecified)\n                .into(),\n        );\n        push(\n            SolidColorRenderElement::from_buffer(\n                &inner.csd_shadow_buffer,\n                location - Point::from((inner.csd_shadow_width, inner.csd_shadow_width)).to_f64(),\n                alpha,\n                Kind::Unspecified,\n            )\n            .into(),\n        );\n    }\n\n    fn request_size(\n        &mut self,\n        size: Size<i32, Logical>,\n        mode: SizingMode,\n        _animate: bool,\n        _transaction: Option<Transaction>,\n    ) {\n        self.inner.borrow_mut().requested_size = Some(size);\n        self.inner.borrow_mut().pending_sizing_mode = mode;\n    }\n\n    fn min_size(&self) -> Size<i32, Logical> {\n        self.inner.borrow().min_size\n    }\n\n    fn max_size(&self) -> Size<i32, Logical> {\n        self.inner.borrow().max_size\n    }\n\n    fn is_wl_surface(&self, _wl_surface: &WlSurface) -> bool {\n        false\n    }\n\n    fn set_preferred_scale_transform(&self, _scale: output::Scale, _transform: Transform) {}\n\n    fn has_ssd(&self) -> bool {\n        false\n    }\n\n    fn output_enter(&self, _output: &Output) {}\n\n    fn output_leave(&self, _output: &Output) {}\n\n    fn set_offscreen_data(&self, _data: Option<OffscreenData>) {}\n\n    fn set_activated(&mut self, _active: bool) {}\n\n    fn set_active_in_column(&mut self, _active: bool) {}\n\n    fn set_floating(&mut self, _floating: bool) {}\n\n    fn set_bounds(&self, _bounds: Size<i32, Logical>) {}\n\n    fn is_ignoring_opacity_window_rule(&self) -> bool {\n        false\n    }\n\n    fn configure_intent(&self) -> ConfigureIntent {\n        ConfigureIntent::CanSend\n    }\n\n    fn send_pending_configure(&mut self) {}\n\n    fn pending_sizing_mode(&self) -> SizingMode {\n        self.inner.borrow().pending_sizing_mode\n    }\n\n    fn sizing_mode(&self) -> SizingMode {\n        SizingMode::Normal\n    }\n\n    fn requested_size(&self) -> Option<Size<i32, Logical>> {\n        self.inner.borrow().requested_size\n    }\n\n    fn is_child_of(&self, _parent: &Self) -> bool {\n        false\n    }\n\n    fn refresh(&self) {}\n\n    fn rules(&self) -> &ResolvedWindowRules {\n        &self.rules\n    }\n\n    fn take_animation_snapshot(&mut self) -> Option<LayoutElementRenderSnapshot> {\n        None\n    }\n\n    fn set_interactive_resize(&mut self, _data: Option<InteractiveResizeData>) {}\n\n    fn cancel_interactive_resize(&mut self) {}\n\n    fn on_commit(&mut self, _serial: Serial) {}\n\n    fn interactive_resize_data(&self) -> Option<InteractiveResizeData> {\n        None\n    }\n\n    fn is_urgent(&self) -> bool {\n        false\n    }\n}\n"
  },
  {
    "path": "niri.spec.rpkg",
    "content": "%bcond_without check\n\n%global cargo_install_lib 0\n\n# We want panic backtraces to work without installing the debuginfo package,\n# so we leave the debuginfo in the main binary.\n%global debug_package %{nil}\n%global __strip /bin/true\n\n# To reduce the file size, do some convincing of rust-srpm-macros\n# to leave alone the chosen debug settings from Cargo.toml.\n%global rustflags_debuginfo please-remove-me\n%global build_rustflags %{shrink:\n  -Copt-level=%rustflags_opt_level\n  -Ccodegen-units=%rustflags_codegen_units\n  -Cstrip=none\n  %{expr:0%{?_include_frame_pointers} && (\"%{_arch}\" != \"ppc64le\" && \"%{_arch}\" != \"s390x\" && \"%{_arch}\" != \"i386\") ? \"-Cforce-frame-pointers=yes\" : \"\"}\n  -Clink-arg=-Wl,-z,relro\n  -Clink-arg=-Wl,-z,now\n  %[0%{?_package_note_status} ? \"-Clink-arg=%_package_note_flags\" : \"\"]\n  --cap-lints=warn\n}\n\n# Convince rust-srpm-macros to use Cargo.lock with the Smithay commit.\n%global __cargo_common_opts %{?_smp_mflags} -Z avoid-dev-deps --locked\n\n%global version {{{ git_dir_version }}}\n\nName:           niri\nVersion:        %{version}\nRelease:        1%{?dist}\nSummary:        Scrollable-tiling Wayland compositor\n\nSourceLicense:  GPL-3.0-or-later\n\n# (MIT OR Apache-2.0) AND BSD-3-Clause\n# (MIT OR Apache-2.0) AND Unicode-3.0\n# 0BSD OR MIT OR Apache-2.0\n# Apache-2.0\n# Apache-2.0 AND MIT\n# Apache-2.0 OR BSL-1.0\n# Apache-2.0 OR MIT\n# Apache-2.0 OR MIT OR Unlicense\n# Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT\n# BSD-2-Clause\n# BSD-2-Clause OR Apache-2.0 OR MIT\n# BSD-3-Clause OR MIT OR Apache-2.0\n# GPL-3.0-or-later\n# ISC\n# MIT\n# MIT OR Apache-2.0\n# MIT OR Apache-2.0 OR LGPL-2.1-or-later\n# MIT OR Apache-2.0 OR Zlib\n# MIT OR Zlib OR Apache-2.0\n# MPL-2.0\n# Unicode-3.0\n# Unlicense OR MIT\n# Zlib\n# Zlib OR Apache-2.0 OR MIT\nLicense:        ((MIT OR Apache-2.0) AND BSD-3-Clause) AND ((MIT OR Apache-2.0) AND Unicode-3.0) AND (0BSD OR MIT OR Apache-2.0) AND (Apache-2.0) AND (Apache-2.0 AND MIT) AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR MIT) AND (Apache-2.0 OR MIT OR Unlicense) AND (Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT) AND (BSD-2-Clause) AND (BSD-2-Clause OR Apache-2.0 OR MIT) AND (BSD-3-Clause OR MIT OR Apache-2.0) AND (GPL-3.0-or-later) AND (ISC) AND (MIT) AND (MIT OR Apache-2.0) AND (MIT OR Apache-2.0 OR LGPL-2.1-or-later) AND (MIT OR Apache-2.0 OR Zlib) AND (MIT OR Zlib OR Apache-2.0) AND (MPL-2.0) AND (Unicode-3.0) AND (Unlicense OR MIT) AND (Zlib) AND (Zlib OR Apache-2.0 OR MIT)\n# LICENSE.dependencies contains a full license breakdown\n\nURL:            https://github.com/niri-wm/niri\nVCS:            {{{ git_dir_vcs }}}\nSource:         {{{ git_dir_pack }}}\n\nBuildRequires:  cargo-rpm-macros >= 26\nBuildRequires:  pkgconfig(udev)\nBuildRequires:  pkgconfig(gbm)\nBuildRequires:  pkgconfig(xkbcommon)\nBuildRequires:  wayland-devel\nBuildRequires:  pkgconfig(libinput)\nBuildRequires:  pkgconfig(dbus-1)\nBuildRequires:  pkgconfig(systemd)\nBuildRequires:  pkgconfig(libseat)\nBuildRequires:  pkgconfig(libdisplay-info)\nBuildRequires:  pipewire-devel\nBuildRequires:  pango-devel\nBuildRequires:  cairo-gobject-devel\n# Needed for pipewire-rs\nBuildRequires:  clang\n# Needed for some tests with a surfaceless EGL renderer\nBuildRequires:  mesa-libEGL\n\nRequires:       mesa-dri-drivers\nRequires:       mesa-libEGL\n\n# Loaded through dlopen\nRequires:       libwayland-server\n\n# Integrated Xwayland support. Not packaged on EPEL\n%if 0%{?fedora}\nRequires:       xwayland-satellite >= 0.7\n%endif\n\n# Portal implementations used by niri\nRecommends:     xdg-desktop-portal-gtk\nRecommends:     xdg-desktop-portal-gnome\nRecommends:     gnome-keyring\n\n# Suggested utilities, bound in the default config\nRecommends:     alacritty\nRecommends:     fuzzel\nRecommends:     swaylock\nRecommends:     waybar\n# Suggested utilities\nRecommends:     swaybg\nRecommends:     mako\nRecommends:     swayidle\n\n%description\nA scrollable-tiling Wayland compositor.\n\nWindows are arranged in columns on an infinite strip going to the right.\nOpening a new window never causes existing windows to resize.\n\n%prep\n{{{ git_dir_setup_macro }}}\n\n%cargo_prep -N\n\n# We're doing an online build.\nsed -i 's/^offline = true$//' .cargo/config.toml\n\n# Final step in leaving alone our debug settings.\nsed -i 's/.*please-remove-me$//' .cargo/config.toml\n\n# Set the commit string.\nsed -i 's/\\[env\\]/[env]\\nNIRI_BUILD_COMMIT=\"%{version}\"/' .cargo/config.toml\n\n%build\n%cargo_build\n\ntarget/rpm/niri completions bash > ./niri\ntarget/rpm/niri completions fish > ./niri.fish\ntarget/rpm/niri completions zsh > ./_niri\n\n%install\n%cargo_install\n\ninstall -Dm755 -t %{buildroot}%{_bindir} ./resources/niri-session\ninstall -Dm644 -t %{buildroot}%{_datadir}/wayland-sessions ./resources/niri.desktop\ninstall -Dm644 -t %{buildroot}%{_datadir}/xdg-desktop-portal ./resources/niri-portals.conf\ninstall -Dm644 -t %{buildroot}%{_userunitdir} ./resources/niri.service\ninstall -Dm644 -t %{buildroot}%{_userunitdir} ./resources/niri-shutdown.target\n\ninstall -Dm644 -t %{buildroot}%{bash_completions_dir} ./niri\ninstall -Dm644 -t %{buildroot}%{fish_completions_dir} ./niri.fish\ninstall -Dm644 -t %{buildroot}%{zsh_completions_dir} ./_niri\n\n%if %{with check}\n%check\n%cargo_test -- --workspace --exclude niri-visual-tests\n%endif\n\n%files\n%license LICENSE\n%doc README.md\n%doc resources/default-config.kdl\n%doc docs/wiki\n%{_bindir}/niri\n%{_bindir}/niri-session\n%{_datadir}/wayland-sessions/niri.desktop\n%dir %{_datadir}/xdg-desktop-portal\n%{_datadir}/xdg-desktop-portal/niri-portals.conf\n%{_userunitdir}/niri.service\n%{_userunitdir}/niri-shutdown.target\n%{bash_completions_dir}/niri\n%{fish_completions_dir}/niri.fish\n%{zsh_completions_dir}/_niri\n\n%changelog\n{{{ git_dir_changelog }}}\n\n"
  },
  {
    "path": "resources/default-config.kdl",
    "content": "// This config is in the KDL format: https://kdl.dev\n// \"/-\" comments out the following node.\n// Check the wiki for a full description of the configuration:\n// https://niri-wm.github.io/niri/Configuration:-Introduction\n\n// Input device configuration.\n// Find the full list of options on the wiki:\n// https://niri-wm.github.io/niri/Configuration:-Input\ninput {\n    keyboard {\n        xkb {\n            // You can set rules, model, layout, variant and options.\n            // For more information, see xkeyboard-config(7).\n\n            // For example:\n            // layout \"us,ru\"\n            // options \"grp:win_space_toggle,compose:ralt,ctrl:nocaps\"\n\n            // If this section is empty, niri will fetch xkb settings\n            // from org.freedesktop.locale1. You can control these using\n            // localectl set-x11-keymap.\n        }\n\n        // Enable numlock on startup, omitting this setting disables it.\n        numlock\n    }\n\n    // Next sections include libinput settings.\n    // Omitting settings disables them, or leaves them at their default values.\n    // All commented-out settings here are examples, not defaults.\n    touchpad {\n        // off\n        tap\n        // dwt\n        // dwtp\n        // drag false\n        // drag-lock\n        natural-scroll\n        // accel-speed 0.2\n        // accel-profile \"flat\"\n        // scroll-method \"two-finger\"\n        // disabled-on-external-mouse\n    }\n\n    mouse {\n        // off\n        // natural-scroll\n        // accel-speed 0.2\n        // accel-profile \"flat\"\n        // scroll-method \"no-scroll\"\n    }\n\n    trackpoint {\n        // off\n        // natural-scroll\n        // accel-speed 0.2\n        // accel-profile \"flat\"\n        // scroll-method \"on-button-down\"\n        // scroll-button 273\n        // scroll-button-lock\n        // middle-emulation\n    }\n\n    // Uncomment this to make the mouse warp to the center of newly focused windows.\n    // warp-mouse-to-focus\n\n    // Focus windows and outputs automatically when moving the mouse into them.\n    // Setting max-scroll-amount=\"0%\" makes it work only on windows already fully on screen.\n    // focus-follows-mouse max-scroll-amount=\"0%\"\n}\n\n// You can configure outputs by their name, which you can find\n// by running `niri msg outputs` while inside a niri instance.\n// The built-in laptop monitor is usually called \"eDP-1\".\n// Find more information on the wiki:\n// https://niri-wm.github.io/niri/Configuration:-Outputs\n// Remember to uncomment the node by removing \"/-\"!\n/-output \"eDP-1\" {\n    // Uncomment this line to disable this output.\n    // off\n\n    // Resolution and, optionally, refresh rate of the output.\n    // The format is \"<width>x<height>\" or \"<width>x<height>@<refresh rate>\".\n    // If the refresh rate is omitted, niri will pick the highest refresh rate\n    // for the resolution.\n    // If the mode is omitted altogether or is invalid, niri will pick one automatically.\n    // Run `niri msg outputs` while inside a niri instance to list all outputs and their modes.\n    mode \"1920x1080@120.030\"\n\n    // You can use integer or fractional scale, for example use 1.5 for 150% scale.\n    scale 2\n\n    // Transform allows to rotate the output counter-clockwise, valid values are:\n    // normal, 90, 180, 270, flipped, flipped-90, flipped-180 and flipped-270.\n    transform \"normal\"\n\n    // Position of the output in the global coordinate space.\n    // This affects directional monitor actions like \"focus-monitor-left\", and cursor movement.\n    // The cursor can only move between directly adjacent outputs.\n    // Output scale and rotation has to be taken into account for positioning:\n    // outputs are sized in logical, or scaled, pixels.\n    // For example, a 3840×2160 output with scale 2.0 will have a logical size of 1920×1080,\n    // so to put another output directly adjacent to it on the right, set its x to 1920.\n    // If the position is unset or results in an overlap, the output is instead placed\n    // automatically.\n    position x=1280 y=0\n}\n\n// Settings that influence how windows are positioned and sized.\n// Find more information on the wiki:\n// https://niri-wm.github.io/niri/Configuration:-Layout\nlayout {\n    // Set gaps around windows in logical pixels.\n    gaps 16\n\n    // When to center a column when changing focus, options are:\n    // - \"never\", default behavior, focusing an off-screen column will keep at the left\n    //   or right edge of the screen.\n    // - \"always\", the focused column will always be centered.\n    // - \"on-overflow\", focusing a column will center it if it doesn't fit\n    //   together with the previously focused column.\n    center-focused-column \"never\"\n\n    // You can customize the widths that \"switch-preset-column-width\" (Mod+R) toggles between.\n    preset-column-widths {\n        // Proportion sets the width as a fraction of the output width, taking gaps into account.\n        // For example, you can perfectly fit four windows sized \"proportion 0.25\" on an output.\n        // The default preset widths are 1/3, 1/2 and 2/3 of the output.\n        proportion 0.33333\n        proportion 0.5\n        proportion 0.66667\n\n        // Fixed sets the width in logical pixels exactly.\n        // fixed 1920\n    }\n\n    // You can also customize the heights that \"switch-preset-window-height\" (Mod+Shift+R) toggles between.\n    // preset-window-heights { }\n\n    // You can change the default width of the new windows.\n    default-column-width { proportion 0.5; }\n    // If you leave the brackets empty, the windows themselves will decide their initial width.\n    // default-column-width {}\n\n    // By default focus ring and border are rendered as a solid background rectangle\n    // behind windows. That is, they will show up through semitransparent windows.\n    // This is because windows using client-side decorations can have an arbitrary shape.\n    //\n    // If you don't like that, you should uncomment `prefer-no-csd` below.\n    // Niri will draw focus ring and border *around* windows that agree to omit their\n    // client-side decorations.\n    //\n    // Alternatively, you can override it with a window rule called\n    // `draw-border-with-background`.\n\n    // You can change how the focus ring looks.\n    focus-ring {\n        // Uncomment this line to disable the focus ring.\n        // off\n\n        // How many logical pixels the ring extends out from the windows.\n        width 4\n\n        // Colors can be set in a variety of ways:\n        // - CSS named colors: \"red\"\n        // - RGB hex: \"#rgb\", \"#rgba\", \"#rrggbb\", \"#rrggbbaa\"\n        // - CSS-like notation: \"rgb(255, 127, 0)\", rgba(), hsl() and a few others.\n\n        // Color of the ring on the active monitor.\n        active-color \"#7fc8ff\"\n\n        // Color of the ring on inactive monitors.\n        //\n        // The focus ring only draws around the active window, so the only place\n        // where you can see its inactive-color is on other monitors.\n        inactive-color \"#505050\"\n\n        // You can also use gradients. They take precedence over solid colors.\n        // Gradients are rendered the same as CSS linear-gradient(angle, from, to).\n        // The angle is the same as in linear-gradient, and is optional,\n        // defaulting to 180 (top-to-bottom gradient).\n        // You can use any CSS linear-gradient tool on the web to set these up.\n        // Changing the color space is also supported, check the wiki for more info.\n        //\n        // active-gradient from=\"#80c8ff\" to=\"#c7ff7f\" angle=45\n\n        // You can also color the gradient relative to the entire view\n        // of the workspace, rather than relative to just the window itself.\n        // To do that, set relative-to=\"workspace-view\".\n        //\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\"\n    }\n\n    // You can also add a border. It's similar to the focus ring, but always visible.\n    border {\n        // The settings are the same as for the focus ring.\n        // If you enable the border, you probably want to disable the focus ring.\n        off\n\n        width 4\n        active-color \"#ffc87f\"\n        inactive-color \"#505050\"\n\n        // Color of the border around windows that request your attention.\n        urgent-color \"#9b0000\"\n\n        // Gradients can use a few different interpolation color spaces.\n        // For example, this is a pastel rainbow gradient via in=\"oklch longer hue\".\n        //\n        // active-gradient from=\"#e5989b\" to=\"#ffb4a2\" angle=45 relative-to=\"workspace-view\" in=\"oklch longer hue\"\n\n        // inactive-gradient from=\"#505050\" to=\"#808080\" angle=45 relative-to=\"workspace-view\"\n    }\n\n    // You can enable drop shadows for windows.\n    shadow {\n        // Uncomment the next line to enable shadows.\n        // on\n\n        // By default, the shadow draws only around its window, and not behind it.\n        // Uncomment this setting to make the shadow draw behind its window.\n        //\n        // Note that niri has no way of knowing about the CSD window corner\n        // radius. It has to assume that windows have square corners, leading to\n        // shadow artifacts inside the CSD rounded corners. This setting fixes\n        // those artifacts.\n        //\n        // However, instead you may want to set prefer-no-csd and/or\n        // geometry-corner-radius. Then, niri will know the corner radius and\n        // draw the shadow correctly, without having to draw it behind the\n        // window. These will also remove client-side shadows if the window\n        // draws any.\n        //\n        // draw-behind-window true\n\n        // You can change how shadows look. The values below are in logical\n        // pixels and match the CSS box-shadow properties.\n\n        // Softness controls the shadow blur radius.\n        softness 30\n\n        // Spread expands the shadow.\n        spread 5\n\n        // Offset moves the shadow relative to the window.\n        offset x=0 y=5\n\n        // You can also change the shadow color and opacity.\n        color \"#0007\"\n    }\n\n    // Struts shrink the area occupied by windows, similarly to layer-shell panels.\n    // You can think of them as a kind of outer gaps. They are set in logical pixels.\n    // Left and right struts will cause the next window to the side to always be visible.\n    // Top and bottom struts will simply add outer gaps in addition to the area occupied by\n    // layer-shell panels and regular gaps.\n    struts {\n        // left 64\n        // right 64\n        // top 64\n        // bottom 64\n    }\n}\n\n// Add lines like this to spawn processes at startup.\n// Note that running niri as a session supports xdg-desktop-autostart,\n// which may be more convenient to use.\n// See the binds section below for more spawn examples.\n\n// This line starts waybar, a commonly used bar for Wayland compositors.\nspawn-at-startup \"waybar\"\n\n// To run a shell command (with variables, pipes, etc.), use spawn-sh-at-startup:\n// spawn-sh-at-startup \"qs -c ~/source/qs/MyAwesomeShell\"\n\nhotkey-overlay {\n    // Uncomment this line to disable the \"Important Hotkeys\" pop-up at startup.\n    // skip-at-startup\n}\n\n// Uncomment this line to ask the clients to omit their client-side decorations if possible.\n// If the client will specifically ask for CSD, the request will be honored.\n// Additionally, clients will be informed that they are tiled, removing some client-side rounded corners.\n// This option will also fix border/focus ring drawing behind some semitransparent windows.\n// After enabling or disabling this, you need to restart the apps for this to take effect.\n// prefer-no-csd\n\n// You can change the path where screenshots are saved.\n// A ~ at the front will be expanded to the home directory.\n// The path is formatted with strftime(3) to give you the screenshot date and time.\nscreenshot-path \"~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png\"\n\n// You can also set this to null to disable saving screenshots to disk.\n// screenshot-path null\n\n// Animation settings.\n// The wiki explains how to configure individual animations:\n// https://niri-wm.github.io/niri/Configuration:-Animations\nanimations {\n    // Uncomment to turn off all animations.\n    // off\n\n    // Slow down all animations by this factor. Values below 1 speed them up instead.\n    // slowdown 3.0\n}\n\n// Window rules let you adjust behavior for individual windows.\n// Find more information on the wiki:\n// https://niri-wm.github.io/niri/Configuration:-Window-Rules\n\n// Work around WezTerm's initial configure bug\n// by setting an empty default-column-width.\nwindow-rule {\n    // This regular expression is intentionally made as specific as possible,\n    // since this is the default config, and we want no false positives.\n    // You can get away with just app-id=\"wezterm\" if you want.\n    match app-id=r#\"^org\\.wezfurlong\\.wezterm$\"#\n    default-column-width {}\n}\n\n// Open the Firefox picture-in-picture player as floating by default.\nwindow-rule {\n    // This app-id regular expression will work for both:\n    // - host Firefox (app-id is \"firefox\")\n    // - Flatpak Firefox (app-id is \"org.mozilla.firefox\")\n    match app-id=r#\"firefox$\"# title=\"^Picture-in-Picture$\"\n    open-floating true\n}\n\n// Example: block out two password managers from screen capture.\n// (This example rule is commented out with a \"/-\" in front.)\n/-window-rule {\n    match app-id=r#\"^org\\.keepassxc\\.KeePassXC$\"#\n    match app-id=r#\"^org\\.gnome\\.World\\.Secrets$\"#\n\n    block-out-from \"screen-capture\"\n\n    // Use this instead if you want them visible on third-party screenshot tools.\n    // block-out-from \"screencast\"\n}\n\n// Example: enable rounded corners for all windows.\n// (This example rule is commented out with a \"/-\" in front.)\n/-window-rule {\n    geometry-corner-radius 12\n    clip-to-geometry true\n}\n\nbinds {\n    // Keys consist of modifiers separated by + signs, followed by an XKB key name\n    // in the end. To find an XKB name for a particular key, you may use a program\n    // like wev.\n    //\n    // \"Mod\" is a special modifier equal to Super when running on a TTY, and to Alt\n    // when running as a winit window.\n    //\n    // Most actions that you can bind here can also be invoked programmatically with\n    // `niri msg action do-something`.\n\n    // Mod-Shift-/, which is usually the same as Mod-?,\n    // shows a list of important hotkeys.\n    Mod+Shift+Slash { show-hotkey-overlay; }\n\n    // Suggested binds for running programs: terminal, app launcher, screen locker.\n    Mod+T hotkey-overlay-title=\"Open a Terminal: alacritty\" { spawn \"alacritty\"; }\n    Mod+D hotkey-overlay-title=\"Run an Application: fuzzel\" { spawn \"fuzzel\"; }\n    Super+Alt+L hotkey-overlay-title=\"Lock the Screen: swaylock\" { spawn \"swaylock\"; }\n\n    // Use spawn-sh to run a shell command. Do this if you need pipes, multiple commands, etc.\n    // Note: the entire command goes as a single argument. It's passed verbatim to `sh -c`.\n    // For example, this is a standard bind to toggle the screen reader (orca).\n    Super+Alt+S allow-when-locked=true hotkey-overlay-title=null { spawn-sh \"pkill orca || exec orca\"; }\n\n    // Example volume keys mappings for PipeWire & WirePlumber.\n    // The allow-when-locked=true property makes them work even when the session is locked.\n    // Using spawn-sh allows to pass multiple arguments together with the command.\n    // \"-l 1.0\" limits the volume to 100%.\n    XF86AudioRaiseVolume allow-when-locked=true { spawn-sh \"wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+ -l 1.0\"; }\n    XF86AudioLowerVolume allow-when-locked=true { spawn-sh \"wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1-\"; }\n    XF86AudioMute        allow-when-locked=true { spawn-sh \"wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle\"; }\n    XF86AudioMicMute     allow-when-locked=true { spawn-sh \"wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle\"; }\n\n    // Example media keys mapping using playerctl.\n    // This will work with any MPRIS-enabled media player.\n    XF86AudioPlay        allow-when-locked=true { spawn-sh \"playerctl play-pause\"; }\n    XF86AudioStop        allow-when-locked=true { spawn-sh \"playerctl stop\"; }\n    XF86AudioPrev        allow-when-locked=true { spawn-sh \"playerctl previous\"; }\n    XF86AudioNext        allow-when-locked=true { spawn-sh \"playerctl next\"; }\n\n    // Example brightness key mappings for brightnessctl.\n    // You can use regular spawn with multiple arguments too (to avoid going through \"sh\"),\n    // but you need to manually put each argument in separate \"\" quotes.\n    XF86MonBrightnessUp allow-when-locked=true { spawn \"brightnessctl\" \"--class=backlight\" \"set\" \"+10%\"; }\n    XF86MonBrightnessDown allow-when-locked=true { spawn \"brightnessctl\" \"--class=backlight\" \"set\" \"10%-\"; }\n\n    // Open/close the Overview: a zoomed-out view of workspaces and windows.\n    // You can also move the mouse into the top-left hot corner,\n    // or do a four-finger swipe up on a touchpad.\n    Mod+O repeat=false { toggle-overview; }\n\n    Mod+Q repeat=false { close-window; }\n\n    Mod+Left  { focus-column-left; }\n    Mod+Down  { focus-window-down; }\n    Mod+Up    { focus-window-up; }\n    Mod+Right { focus-column-right; }\n    Mod+H     { focus-column-left; }\n    Mod+J     { focus-window-down; }\n    Mod+K     { focus-window-up; }\n    Mod+L     { focus-column-right; }\n\n    Mod+Ctrl+Left  { move-column-left; }\n    Mod+Ctrl+Down  { move-window-down; }\n    Mod+Ctrl+Up    { move-window-up; }\n    Mod+Ctrl+Right { move-column-right; }\n    Mod+Ctrl+H     { move-column-left; }\n    Mod+Ctrl+J     { move-window-down; }\n    Mod+Ctrl+K     { move-window-up; }\n    Mod+Ctrl+L     { move-column-right; }\n\n    // Alternative commands that move across workspaces when reaching\n    // the first or last window in a column.\n    // Mod+J     { focus-window-or-workspace-down; }\n    // Mod+K     { focus-window-or-workspace-up; }\n    // Mod+Ctrl+J     { move-window-down-or-to-workspace-down; }\n    // Mod+Ctrl+K     { move-window-up-or-to-workspace-up; }\n\n    Mod+Home { focus-column-first; }\n    Mod+End  { focus-column-last; }\n    Mod+Ctrl+Home { move-column-to-first; }\n    Mod+Ctrl+End  { move-column-to-last; }\n\n    Mod+Shift+Left  { focus-monitor-left; }\n    Mod+Shift+Down  { focus-monitor-down; }\n    Mod+Shift+Up    { focus-monitor-up; }\n    Mod+Shift+Right { focus-monitor-right; }\n    Mod+Shift+H     { focus-monitor-left; }\n    Mod+Shift+J     { focus-monitor-down; }\n    Mod+Shift+K     { focus-monitor-up; }\n    Mod+Shift+L     { focus-monitor-right; }\n\n    Mod+Shift+Ctrl+Left  { move-column-to-monitor-left; }\n    Mod+Shift+Ctrl+Down  { move-column-to-monitor-down; }\n    Mod+Shift+Ctrl+Up    { move-column-to-monitor-up; }\n    Mod+Shift+Ctrl+Right { move-column-to-monitor-right; }\n    Mod+Shift+Ctrl+H     { move-column-to-monitor-left; }\n    Mod+Shift+Ctrl+J     { move-column-to-monitor-down; }\n    Mod+Shift+Ctrl+K     { move-column-to-monitor-up; }\n    Mod+Shift+Ctrl+L     { move-column-to-monitor-right; }\n\n    // Alternatively, there are commands to move just a single window:\n    // Mod+Shift+Ctrl+Left  { move-window-to-monitor-left; }\n    // ...\n\n    // And you can also move a whole workspace to another monitor:\n    // Mod+Shift+Ctrl+Left  { move-workspace-to-monitor-left; }\n    // ...\n\n    Mod+Page_Down      { focus-workspace-down; }\n    Mod+Page_Up        { focus-workspace-up; }\n    Mod+U              { focus-workspace-down; }\n    Mod+I              { focus-workspace-up; }\n    Mod+Ctrl+Page_Down { move-column-to-workspace-down; }\n    Mod+Ctrl+Page_Up   { move-column-to-workspace-up; }\n    Mod+Ctrl+U         { move-column-to-workspace-down; }\n    Mod+Ctrl+I         { move-column-to-workspace-up; }\n\n    // Alternatively, there are commands to move just a single window:\n    // Mod+Ctrl+Page_Down { move-window-to-workspace-down; }\n    // ...\n\n    Mod+Shift+Page_Down { move-workspace-down; }\n    Mod+Shift+Page_Up   { move-workspace-up; }\n    Mod+Shift+U         { move-workspace-down; }\n    Mod+Shift+I         { move-workspace-up; }\n\n    // You can bind mouse wheel scroll ticks using the following syntax.\n    // These binds will change direction based on the natural-scroll setting.\n    //\n    // To avoid scrolling through workspaces really fast, you can use\n    // the cooldown-ms property. The bind will be rate-limited to this value.\n    // You can set a cooldown on any bind, but it's most useful for the wheel.\n    Mod+WheelScrollDown      cooldown-ms=150 { focus-workspace-down; }\n    Mod+WheelScrollUp        cooldown-ms=150 { focus-workspace-up; }\n    Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; }\n    Mod+Ctrl+WheelScrollUp   cooldown-ms=150 { move-column-to-workspace-up; }\n\n    Mod+WheelScrollRight      { focus-column-right; }\n    Mod+WheelScrollLeft       { focus-column-left; }\n    Mod+Ctrl+WheelScrollRight { move-column-right; }\n    Mod+Ctrl+WheelScrollLeft  { move-column-left; }\n\n    // Usually scrolling up and down with Shift in applications results in\n    // horizontal scrolling; these binds replicate that.\n    Mod+Shift+WheelScrollDown      { focus-column-right; }\n    Mod+Shift+WheelScrollUp        { focus-column-left; }\n    Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }\n    Mod+Ctrl+Shift+WheelScrollUp   { move-column-left; }\n\n    // Similarly, you can bind touchpad scroll \"ticks\".\n    // Touchpad scrolling is continuous, so for these binds it is split into\n    // discrete intervals.\n    // These binds are also affected by touchpad's natural-scroll, so these\n    // example binds are \"inverted\", since we have natural-scroll enabled for\n    // touchpads by default.\n    // Mod+TouchpadScrollDown { spawn-sh \"wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.02+\"; }\n    // Mod+TouchpadScrollUp   { spawn-sh \"wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.02-\"; }\n\n    // You can refer to workspaces by index. However, keep in mind that\n    // niri is a dynamic workspace system, so these commands are kind of\n    // \"best effort\". Trying to refer to a workspace index bigger than\n    // the current workspace count will instead refer to the bottommost\n    // (empty) workspace.\n    //\n    // For example, with 2 workspaces + 1 empty, indices 3, 4, 5 and so on\n    // will all refer to the 3rd workspace.\n    Mod+1 { focus-workspace 1; }\n    Mod+2 { focus-workspace 2; }\n    Mod+3 { focus-workspace 3; }\n    Mod+4 { focus-workspace 4; }\n    Mod+5 { focus-workspace 5; }\n    Mod+6 { focus-workspace 6; }\n    Mod+7 { focus-workspace 7; }\n    Mod+8 { focus-workspace 8; }\n    Mod+9 { focus-workspace 9; }\n    Mod+Ctrl+1 { move-column-to-workspace 1; }\n    Mod+Ctrl+2 { move-column-to-workspace 2; }\n    Mod+Ctrl+3 { move-column-to-workspace 3; }\n    Mod+Ctrl+4 { move-column-to-workspace 4; }\n    Mod+Ctrl+5 { move-column-to-workspace 5; }\n    Mod+Ctrl+6 { move-column-to-workspace 6; }\n    Mod+Ctrl+7 { move-column-to-workspace 7; }\n    Mod+Ctrl+8 { move-column-to-workspace 8; }\n    Mod+Ctrl+9 { move-column-to-workspace 9; }\n\n    // Alternatively, there are commands to move just a single window:\n    // Mod+Ctrl+1 { move-window-to-workspace 1; }\n\n    // Switches focus between the current and the previous workspace.\n    // Mod+Tab { focus-workspace-previous; }\n\n    // The following binds move the focused window in and out of a column.\n    // If the window is alone, they will consume it into the nearby column to the side.\n    // If the window is already in a column, they will expel it out.\n    Mod+BracketLeft  { consume-or-expel-window-left; }\n    Mod+BracketRight { consume-or-expel-window-right; }\n\n    // Consume one window from the right to the bottom of the focused column.\n    Mod+Comma  { consume-window-into-column; }\n    // Expel the bottom window from the focused column to the right.\n    Mod+Period { expel-window-from-column; }\n\n    Mod+R { switch-preset-column-width; }\n    // Cycling through the presets in reverse order is also possible.\n    // Mod+R { switch-preset-column-width-back; }\n    Mod+Shift+R { switch-preset-window-height; }\n    Mod+Ctrl+R { reset-window-height; }\n    Mod+F { maximize-column; }\n    Mod+Shift+F { fullscreen-window; }\n\n    // While maximize-column leaves gaps and borders around the window,\n    // maximize-window-to-edges doesn't: the window expands to the edges of the screen.\n    // This bind corresponds to normal window maximizing,\n    // e.g. by double-clicking on the titlebar.\n    Mod+M { maximize-window-to-edges; }\n\n    // Expand the focused column to space not taken up by other fully visible columns.\n    // Makes the column \"fill the rest of the space\".\n    Mod+Ctrl+F { expand-column-to-available-width; }\n\n    Mod+C { center-column; }\n\n    // Center all fully visible columns on screen.\n    Mod+Ctrl+C { center-visible-columns; }\n\n    // Finer width adjustments.\n    // This command can also:\n    // * set width in pixels: \"1000\"\n    // * adjust width in pixels: \"-5\" or \"+5\"\n    // * set width as a percentage of screen width: \"25%\"\n    // * adjust width as a percentage of screen width: \"-10%\" or \"+10%\"\n    // Pixel sizes use logical, or scaled, pixels. I.e. on an output with scale 2.0,\n    // set-column-width \"100\" will make the column occupy 200 physical screen pixels.\n    Mod+Minus { set-column-width \"-10%\"; }\n    Mod+Equal { set-column-width \"+10%\"; }\n\n    // Finer height adjustments when in column with other windows.\n    Mod+Shift+Minus { set-window-height \"-10%\"; }\n    Mod+Shift+Equal { set-window-height \"+10%\"; }\n\n    // Move the focused window between the floating and the tiling layout.\n    Mod+V       { toggle-window-floating; }\n    Mod+Shift+V { switch-focus-between-floating-and-tiling; }\n\n    // Toggle tabbed column display mode.\n    // Windows in this column will appear as vertical tabs,\n    // rather than stacked on top of each other.\n    Mod+W { toggle-column-tabbed-display; }\n\n    // Actions to switch layouts.\n    // Note: if you uncomment these, make sure you do NOT have\n    // a matching layout switch hotkey configured in xkb options above.\n    // Having both at once on the same hotkey will break the switching,\n    // since it will switch twice upon pressing the hotkey (once by xkb, once by niri).\n    // Mod+Space       { switch-layout \"next\"; }\n    // Mod+Shift+Space { switch-layout \"prev\"; }\n\n    Print { screenshot; }\n    Ctrl+Print { screenshot-screen; }\n    Alt+Print { screenshot-window; }\n\n    // Applications such as remote-desktop clients and software KVM switches may\n    // request that niri stops processing the keyboard shortcuts defined here\n    // so they may, for example, forward the key presses as-is to a remote machine.\n    // It's a good idea to bind an escape hatch to toggle the inhibitor,\n    // so a buggy application can't hold your session hostage.\n    //\n    // The allow-inhibiting=false property can be applied to other binds as well,\n    // which ensures niri always processes them, even when an inhibitor is active.\n    Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }\n\n    // The quit action will show a confirmation dialog to avoid accidental exits.\n    Mod+Shift+E { quit; }\n    Ctrl+Alt+Delete { quit; }\n\n    // Powers off the monitors. To turn them back on, do any input like\n    // moving the mouse or pressing any other key.\n    Mod+Shift+P { power-off-monitors; }\n}\n"
  },
  {
    "path": "resources/dinit/niri",
    "content": "type               = process\ncommand            = niri --session\nrestart            = false\nworking-dir        = $HOME\nready-notification = pipevar:NOTIFY_FD\nlogfile            = $HOME/.local/share/niri/niri.log\ndepends-on:        dbus\n"
  },
  {
    "path": "resources/dinit/niri.target",
    "content": "type           = internal\nrestart        = false\ndepends-on:    niri\nwaits-for.d:   $XDG_CONFIG_HOME/dinit.d/niri.d/\nwaits-for.d:   $HOME/.config/dinit.d/niri.d/\nwaits-for.d:   /etc/dinit.d/user/niri.d/\n"
  },
  {
    "path": "resources/mutter-x11-interop.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<protocol name=\"mutter_x11_interop\">\n  <description summary=\"X11 interoperability helper\">\n    This protocol is intended to be used by the portal backend to map Wayland\n    dialogs as modal dialogs on top of X11 windows.\n  </description>\n\n  <interface name=\"mutter_x11_interop\" version=\"1\">\n    <description summary=\"X11 interoperability helper\"/>\n\n    <request name=\"destroy\" type=\"destructor\"/>\n\n    <request name=\"set_x11_parent\">\n      <arg name=\"surface\" type=\"object\" interface=\"wl_surface\"/>\n      <arg name=\"xwindow\" type=\"uint\"/>\n    </request>\n  </interface>\n</protocol>\n"
  },
  {
    "path": "resources/niri-portals.conf",
    "content": "[preferred]\ndefault=gnome;gtk;\norg.freedesktop.impl.portal.Access=gtk;\norg.freedesktop.impl.portal.Notification=gtk;\norg.freedesktop.impl.portal.Secret=gnome-keyring;\n"
  },
  {
    "path": "resources/niri-session",
    "content": "#!/bin/sh\n\n# Detect if being run as a user service, which implies external session management,\n# exec compositor directly\nif [ -n \"${MANAGERPID:-}\" ] && [ \"${SYSTEMD_EXEC_PID:-}\" = \"$$\" ]; then\n    case \"$(ps -p \"$MANAGERPID\" -o cmd=)\" in\n    *systemd*--user*)\n        exec niri --session\n        ;;\n    esac\nfi\n\nif [ -n \"$SHELL\" ] &&\n   grep -q \"$SHELL\" /etc/shells &&\n   ! (echo \"$SHELL\" | grep -q \"false\") &&\n   ! (echo \"$SHELL\" | grep -q \"nologin\"); then\n  if [ \"$1\" != '-l' ]; then\n    exec bash -c \"exec -l '$SHELL' -c '$0 -l $*'\"\n  else\n    shift\n  fi\nfi\n\n# Try to detect the service manager that is being used\nif hash systemctl >/dev/null 2>&1; then\n    # Make sure there's no already running session.\n    if systemctl --user -q is-active niri.service; then\n      echo 'A niri session is already running.'\n      exit 1\n    fi\n\n    # Reset failed state of all user units.\n    systemctl --user reset-failed\n\n    # Import the login manager environment.\n    systemctl --user import-environment\n\n    # DBus activation environment is independent from systemd. While most of\n    # dbus-activated services are already using `SystemdService` directive, some\n    # still don't and thus we should set the dbus environment with a separate\n    # command.\n    if hash dbus-update-activation-environment 2>/dev/null; then\n        dbus-update-activation-environment --all\n    fi\n\n    # Start niri and wait for it to terminate.\n    systemctl --user --wait start niri.service\n\n    # Force stop of graphical-session.target.\n    systemctl --user start --job-mode=replace-irreversibly niri-shutdown.target\n\n    # Unset environment that we've set.\n    systemctl --user unset-environment WAYLAND_DISPLAY DISPLAY XDG_SESSION_TYPE XDG_CURRENT_DESKTOP NIRI_SOCKET\nelif hash dinitctl >/dev/null 2>&1; then\n    # Check that the user dinit daemon is running\n    if ! pgrep -u \"$(id -u)\" dinit >/dev/null 2>&1; then\n      echo \"dinit user daemon is not running.\"\n      exit 1\n    fi\n\n    # Make sure there's no already running session.\n    if dinitctl --quiet --user is-started niri 2>/dev/null; then\n      echo 'A niri session is already running.'\n      exit 1\n    fi\n\n    # Import the login manager environment into dinit\n    # Might not work correctly for multiline variable names, but\n    # it is reasonable to assume there are none\n    awk 'BEGIN{for(v in ENVIRON) if (v != \"AWKPATH\" && v != \"AWKLIBPATH\") print v}' 2>/dev/null | xargs dinitctl --quiet --user setenv 2>/dev/null\n\n    # Usually the dbus service would start as niri's dependency and inherit\n    # environment from dinit, but in case it has already started we need\n    # to update its environment.\n    if hash dbus-update-activation-environment >/dev/null 2>&1; then\n        dbus-update-activation-environment --all >/dev/null 2>&1\n    fi\n\n    # Create the directory for the logfile, if doesn't exist\n    mkdir --parents $HOME/.local/share/niri\n    # Start niri\n    dinitctl --quiet --user start niri.target 2>&1\n\n    # Wait for termination\n    dinit-monitor --user --initial -c $'sh -c \" \n        if [ \"%s\" = \"stopped\" ] || [ \"%s\" = \"failed\" ]; then\n            ppid=$(ps -o ppid= -p $$)\n            kill $ppid\n        fi\"' niri >/dev/null 2>&1\n\n    # Unset environment that we've set.\n    dinitctl --quiet --user unsetenv WAYLAND_DISPLAY DISPLAY XDG_SESSION_TYPE XDG_CURRENT_DESKTOP NIRI_SOCKET 2>/dev/null\nelse\n    echo \"No systemd or dinit detected, please use niri --session instead.\"\nfi\n"
  },
  {
    "path": "resources/niri-shutdown.target",
    "content": "[Unit]\nDescription=Shutdown running niri session\nDefaultDependencies=no\nStopWhenUnneeded=true\n\nConflicts=graphical-session.target graphical-session-pre.target\nAfter=graphical-session.target graphical-session-pre.target\n"
  },
  {
    "path": "resources/niri.desktop",
    "content": "[Desktop Entry]\nName=Niri\nComment=A scrollable-tiling Wayland compositor\nExec=niri-session\nType=Application\nDesktopNames=niri\n"
  },
  {
    "path": "resources/niri.service",
    "content": "[Unit]\nDescription=A scrollable-tiling Wayland compositor\nBindsTo=graphical-session.target\nBefore=graphical-session.target\nWants=graphical-session-pre.target\nAfter=graphical-session-pre.target\n\nWants=xdg-desktop-autostart.target\nBefore=xdg-desktop-autostart.target\n\n[Service]\nSlice=session.slice\nType=notify\nExecStart=niri --session\n"
  },
  {
    "path": "resources/rustdoc-index.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta http-equiv=refresh content=0;url=niri_ipc/index.html />\n    </head>\n</html>\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "imports_granularity = \"Module\"\ngroup_imports = \"StdExternalCrate\"\nwrap_comments = true\ncomment_width = 100\n"
  },
  {
    "path": "src/a11y.rs",
    "content": "use std::sync::mpsc;\nuse std::thread;\n\nuse accesskit::{\n    ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler, Live, Node, NodeId, Role,\n    Tree, TreeId, TreeUpdate,\n};\nuse accesskit_unix::Adapter;\nuse calloop::LoopHandle;\nuse niri_config::MruScope;\n\nuse crate::layout::workspace::WorkspaceId;\nuse crate::niri::{KeyboardFocus, Niri, State};\nuse crate::utils::with_toplevel_role;\nuse crate::window::mapped::MappedId;\n\nconst ID_ROOT: NodeId = NodeId(0);\nconst ID_ANNOUNCEMENT: NodeId = NodeId(1);\nconst ID_SCREENSHOT_UI: NodeId = NodeId(2);\nconst ID_EXIT_CONFIRM_DIALOG: NodeId = NodeId(3);\nconst ID_OVERVIEW: NodeId = NodeId(4);\nconst ID_MRU: NodeId = NodeId(5);\n\npub struct A11y {\n    event_loop: LoopHandle<'static, State>,\n    focus: NodeId,\n    workspace_id: Option<WorkspaceId>,\n    mru_selection: Option<MappedId>,\n    mru_scope: Option<MruScope>,\n    last_mru_title: String,\n    last_announcement: String,\n    to_accesskit: Option<mpsc::SyncSender<TreeUpdate>>,\n}\n\nenum Msg {\n    InitialTree,\n    Deactivate,\n    Action(ActionRequest),\n}\n\nimpl A11y {\n    pub fn new(event_loop: LoopHandle<'static, State>) -> Self {\n        Self {\n            event_loop,\n            focus: ID_ROOT,\n            workspace_id: None,\n            mru_selection: None,\n            mru_scope: None,\n            last_mru_title: String::new(),\n            last_announcement: String::new(),\n            to_accesskit: None,\n        }\n    }\n\n    pub fn start(&mut self) {\n        let (tx, rx) = calloop::channel::channel();\n        let (to_accesskit, from_main) = mpsc::sync_channel::<TreeUpdate>(8);\n\n        // The adapter has a tendency to deadlock, so put it on a thread for now...\n        let handler = Handler { tx };\n        let res = thread::Builder::new()\n            .name(\"AccessKit Adapter\".to_owned())\n            .spawn(move || {\n                let mut adapter = Adapter::new(handler.clone(), handler.clone(), handler);\n                while let Ok(tree) = from_main.recv() {\n                    let is_focused = tree.focus != ID_ROOT;\n                    adapter.update_if_active(move || tree);\n                    adapter.update_window_focus_state(is_focused);\n                }\n            });\n\n        match res {\n            Ok(_handle) => {}\n            Err(err) => {\n                warn!(\"error spawning the AccessKit adapter thread: {err:?}\");\n                return;\n            }\n        }\n\n        self.event_loop\n            .insert_source(rx, |e, _, state| match e {\n                calloop::channel::Event::Msg(msg) => state.niri.on_a11y_msg(msg),\n                calloop::channel::Event::Closed => (),\n            })\n            .unwrap();\n\n        self.to_accesskit = Some(to_accesskit);\n    }\n\n    fn update_tree(&mut self, tree: TreeUpdate) {\n        trace!(\"updating tree: {tree:?}\");\n        self.focus = tree.focus;\n\n        let Some(tx) = &mut self.to_accesskit else {\n            return;\n        };\n        match tx.try_send(tree) {\n            Ok(()) => {}\n            Err(mpsc::TrySendError::Full(_)) => {\n                warn!(\"AccessKit channel is full, it probably deadlocked; disconnecting\");\n                self.to_accesskit = None;\n            }\n            Err(mpsc::TrySendError::Disconnected(_)) => {\n                warn!(\"AccessKit channel disconnected\");\n                self.to_accesskit = None;\n            }\n        }\n    }\n}\n\nimpl Niri {\n    pub fn refresh_a11y(&mut self) {\n        if self.a11y.to_accesskit.is_none() {\n            return;\n        }\n\n        let _span = tracy_client::span!(\"refresh_a11y\");\n\n        let mut announcement = None;\n        let ws_id = self.layout.active_workspace().map(|ws| ws.id());\n        if let Some(ws_id) = ws_id {\n            if self.a11y.workspace_id != Some(ws_id) {\n                let (_, idx, ws) = self\n                    .layout\n                    .workspaces()\n                    .find(|(_, _, ws)| ws.id() == ws_id)\n                    .unwrap();\n\n                let mut buf = format!(\"Workspace {}\", idx + 1);\n                if let Some(name) = ws.name() {\n                    buf.push(' ');\n                    buf.push_str(name);\n                }\n\n                announcement = Some(buf);\n            }\n        }\n        self.a11y.workspace_id = ws_id;\n\n        let focus = self.a11y_focus();\n\n        // Check if the MRU selection changed.\n        let mut update_mru_selection = false;\n        if focus == ID_MRU {\n            let current = self.window_mru_ui.current_window_id();\n            if self.a11y.mru_selection != current {\n                update_mru_selection = true;\n                self.a11y.mru_selection = current;\n            }\n\n            // If there's no window title to announce, check if there's a scope change.\n            let scope = self.window_mru_ui.scope();\n            if !update_mru_selection && self.a11y.mru_scope != Some(scope) {\n                announcement = Some(self.window_mru_ui.a11y_scope_text());\n            }\n            self.a11y.mru_scope = Some(scope);\n        } else {\n            self.a11y.mru_scope = None;\n            self.a11y.mru_selection = None;\n        }\n\n        let update_focus = self.a11y.focus != focus;\n\n        if !(announcement.is_some() || update_focus || update_mru_selection) {\n            return;\n        }\n\n        let mut nodes = Vec::new();\n\n        if let Some(mut announcement) = announcement {\n            // Work around having to change node value for it to get announced.\n            if announcement == self.a11y.last_announcement {\n                announcement.push(' ');\n            }\n            self.a11y.last_announcement = announcement.clone();\n\n            let mut node = Node::new(Role::Label);\n            node.set_value(announcement);\n            node.set_live(Live::Polite);\n            nodes.push((ID_ANNOUNCEMENT, node));\n        }\n\n        if focus == ID_MRU {\n            // Ideally MRU would be a Group with a child Button for a window, but I've no idea how\n            // to make it work reliably. When I did it that way, there were two issues:\n            //\n            // 1. Alt-Tab would always start reading from \"Recent windows grouping\" instead of the\n            //    window title.\n            // 2. When Alt-Tab became empty (e.g. switching scope to something empty), Orca would\n            //    completely stop reading any child buttons for the remainder of the session.\n            //\n            // I've no idea what to do about these and where they even come from. So, just flip the\n            // MRU node between Group and Button, which seems to work fine.\n            if update_mru_selection {\n                if let Some(id) = self.a11y.mru_selection {\n                    if let Some((_, mapped)) = self.layout.windows().find(|(_, m)| m.id() == id) {\n                        with_toplevel_role(mapped.toplevel(), |role| {\n                            let mut title = role.title.as_deref().unwrap_or(\"Unknown\").to_owned();\n                            // Change title on match to ensure we announce same-titled windows.\n                            if self.a11y.last_mru_title == title {\n                                title.push(' ');\n                            }\n                            self.a11y.last_mru_title = title;\n\n                            let mut mru = Node::new(Role::Button);\n                            mru.set_label(&*self.a11y.last_mru_title);\n                            nodes.push((ID_MRU, mru));\n                        });\n                    }\n                } else {\n                    let mut mru = Node::new(Role::Group);\n                    // Announce the current scope in the empty text to make it clear.\n                    let scope = self.window_mru_ui.a11y_scope_text();\n                    mru.set_label(format!(\"Recent windows empty, {scope}\"));\n                    nodes.push((ID_MRU, mru));\n                }\n            }\n        }\n\n        let update = TreeUpdate {\n            nodes,\n            tree: None,\n            tree_id: TreeId::ROOT,\n            focus,\n        };\n\n        self.a11y.update_tree(update);\n    }\n\n    pub fn a11y_announce(&mut self, mut announcement: String) {\n        if self.a11y.to_accesskit.is_none() {\n            return;\n        }\n\n        let _span = tracy_client::span!(\"a11y_announce\");\n\n        // Work around having to change node value for it to get announced.\n        if announcement == self.a11y.last_announcement {\n            announcement.push(' ');\n        }\n        self.a11y.last_announcement = announcement.clone();\n\n        let mut node = Node::new(Role::Label);\n        node.set_value(announcement);\n        node.set_live(Live::Polite);\n\n        let update = TreeUpdate {\n            nodes: vec![(ID_ANNOUNCEMENT, node)],\n            tree: None,\n            tree_id: TreeId::ROOT,\n            focus: self.a11y.focus,\n        };\n\n        self.a11y.update_tree(update);\n    }\n\n    pub fn a11y_announce_config_error(&mut self) {\n        if self.a11y.to_accesskit.is_none() {\n            return;\n        }\n\n        self.a11y_announce(crate::ui::config_error_notification::error_text(false));\n    }\n\n    pub fn a11y_announce_hotkey_overlay(&mut self) {\n        if self.a11y.to_accesskit.is_none() {\n            return;\n        }\n\n        self.a11y_announce(self.hotkey_overlay.a11y_text());\n    }\n\n    fn a11y_focus(&self) -> NodeId {\n        match self.keyboard_focus {\n            KeyboardFocus::ScreenshotUi => ID_SCREENSHOT_UI,\n            KeyboardFocus::ExitConfirmDialog => ID_EXIT_CONFIRM_DIALOG,\n            KeyboardFocus::Overview => ID_OVERVIEW,\n            KeyboardFocus::Mru => ID_MRU,\n            _ => ID_ROOT,\n        }\n    }\n\n    fn on_a11y_msg(&mut self, msg: Msg) {\n        match msg {\n            Msg::InitialTree => {\n                let tree = self.a11y_build_full_tree();\n                trace!(\"sending initial tree: {tree:?}\");\n                self.a11y.update_tree(tree);\n            }\n            Msg::Deactivate => {\n                trace!(\"deactivate\");\n            }\n            Msg::Action(request) => {\n                trace!(\"request: {request:?}\");\n            }\n        }\n    }\n\n    fn a11y_build_full_tree(&self) -> TreeUpdate {\n        let mut node = Node::new(Role::Label);\n        node.set_live(Live::Polite);\n\n        let mut screenshot_ui = Node::new(Role::Group);\n        screenshot_ui.set_label(\"Screenshot UI\");\n\n        let exit_confirm_dialog = crate::ui::exit_confirm_dialog::a11y_node();\n\n        let mut overview = Node::new(Role::Group);\n        overview.set_label(\"Overview\");\n\n        let mut mru = Node::new(Role::Group);\n        mru.set_label(\"Recent windows\");\n\n        let mut root = Node::new(Role::Window);\n        root.set_children(vec![\n            ID_ANNOUNCEMENT,\n            ID_SCREENSHOT_UI,\n            ID_EXIT_CONFIRM_DIALOG,\n            ID_OVERVIEW,\n            ID_MRU,\n        ]);\n\n        let tree = Tree {\n            root: ID_ROOT,\n            toolkit_name: Some(String::from(\"niri\")),\n            toolkit_version: None,\n        };\n\n        let focus = self.a11y_focus();\n\n        // NOTE: we don't fill in current MRU selection here to avoid duplicating code; it should\n        // get updated right away anyway.\n\n        TreeUpdate {\n            nodes: vec![\n                (ID_ROOT, root),\n                (ID_ANNOUNCEMENT, node),\n                (ID_SCREENSHOT_UI, screenshot_ui),\n                (ID_EXIT_CONFIRM_DIALOG, exit_confirm_dialog),\n                (ID_OVERVIEW, overview),\n                (ID_MRU, mru),\n            ],\n            tree: Some(tree),\n            tree_id: TreeId::ROOT,\n            focus,\n        }\n    }\n}\n\n#[derive(Clone)]\nstruct Handler {\n    tx: calloop::channel::Sender<Msg>,\n}\n\nimpl ActivationHandler for Handler {\n    fn request_initial_tree(&mut self) -> Option<TreeUpdate> {\n        let _ = self.tx.send(Msg::InitialTree);\n        None\n    }\n}\n\nimpl DeactivationHandler for Handler {\n    fn deactivate_accessibility(&mut self) {\n        let _ = self.tx.send(Msg::Deactivate);\n    }\n}\n\nimpl ActionHandler for Handler {\n    fn do_action(&mut self, request: ActionRequest) {\n        let _ = self.tx.send(Msg::Action(request));\n    }\n}\n"
  },
  {
    "path": "src/animation/bezier.rs",
    "content": "use keyframe::EasingFunction;\n\n#[derive(Debug, Clone, Copy)]\npub struct CubicBezier {\n    x1: f64,\n    y1: f64,\n    x2: f64,\n    y2: f64,\n}\n\nimpl CubicBezier {\n    pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {\n        Self { x1, y1, x2, y2 }\n    }\n\n    // Based on libadwaita (LGPL-2.1-or-later):\n    // https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.7.6/src/adw-easing.c?ref_type=tags#L469-531\n\n    fn x_for_t(&self, t: f64) -> f64 {\n        let omt = 1. - t;\n        3. * omt * omt * t * self.x1 + 3. * omt * t * t * self.x2 + t * t * t\n    }\n\n    fn y_for_t(&self, t: f64) -> f64 {\n        let omt = 1. - t;\n        3. * omt * omt * t * self.y1 + 3. * omt * t * t * self.y2 + t * t * t\n    }\n\n    fn t_for_x(&self, x: f64) -> f64 {\n        let mut min_t = 0.;\n        let mut max_t = 1.;\n\n        for _ in 0..=30 {\n            let guess_t = (min_t + max_t) / 2.;\n            let guess_x = self.x_for_t(guess_t);\n\n            if x < guess_x {\n                max_t = guess_t;\n            } else {\n                min_t = guess_t;\n            }\n        }\n\n        (min_t + max_t) / 2.\n    }\n}\n\nimpl EasingFunction for CubicBezier {\n    fn y(&self, x: f64) -> f64 {\n        if x <= f64::EPSILON {\n            return 0.;\n        }\n\n        if 1. - f64::EPSILON <= x {\n            return 1.;\n        }\n\n        self.y_for_t(self.t_for_x(x))\n    }\n}\n"
  },
  {
    "path": "src/animation/clock.rs",
    "content": "use std::cell::RefCell;\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse crate::utils::get_monotonic_time;\n\n/// Shareable lazy clock that can change rate.\n///\n/// The clock will fetch the time once and then retain it until explicitly cleared with\n/// [`Clock::clear`].\n#[derive(Debug, Default, Clone)]\npub struct Clock {\n    inner: Rc<RefCell<AdjustableClock>>,\n}\n\n#[derive(Debug, Default)]\nstruct LazyClock {\n    time: Option<Duration>,\n}\n\n/// Clock that can adjust its rate.\n#[derive(Debug)]\nstruct AdjustableClock {\n    inner: LazyClock,\n    current_time: Duration,\n    last_seen_time: Duration,\n    rate: f64,\n    complete_instantly: bool,\n}\n\nimpl Clock {\n    /// Creates a new clock with the given time.\n    pub fn with_time(time: Duration) -> Self {\n        let clock = AdjustableClock::new(LazyClock::with_time(time));\n        Self {\n            inner: Rc::new(RefCell::new(clock)),\n        }\n    }\n\n    /// Returns the current time.\n    pub fn now(&self) -> Duration {\n        self.inner.borrow_mut().now()\n    }\n\n    /// Returns the underlying time not adjusted for rate change.\n    pub fn now_unadjusted(&self) -> Duration {\n        self.inner.borrow_mut().inner.now()\n    }\n\n    /// Sets the unadjusted clock time.\n    pub fn set_unadjusted(&mut self, time: Duration) {\n        self.inner.borrow_mut().inner.set(time);\n    }\n\n    /// Clears the stored time so it's re-fetched again next.\n    pub fn clear(&mut self) {\n        self.inner.borrow_mut().inner.clear();\n    }\n\n    /// Gets the clock rate.\n    pub fn rate(&self) -> f64 {\n        self.inner.borrow().rate()\n    }\n\n    /// Sets the clock rate.\n    pub fn set_rate(&mut self, rate: f64) {\n        self.inner.borrow_mut().set_rate(rate);\n    }\n\n    /// Returns whether animations should complete instantly.\n    pub fn should_complete_instantly(&self) -> bool {\n        self.inner.borrow().should_complete_instantly()\n    }\n\n    /// Sets whether animations should complete instantly.\n    pub fn set_complete_instantly(&mut self, value: bool) {\n        self.inner.borrow_mut().set_complete_instantly(value);\n    }\n}\n\nimpl PartialEq for Clock {\n    fn eq(&self, other: &Self) -> bool {\n        Rc::ptr_eq(&self.inner, &other.inner)\n    }\n}\n\nimpl Eq for Clock {}\n\nimpl LazyClock {\n    pub fn with_time(time: Duration) -> Self {\n        Self { time: Some(time) }\n    }\n\n    pub fn clear(&mut self) {\n        self.time = None;\n    }\n\n    pub fn set(&mut self, time: Duration) {\n        self.time = Some(time);\n    }\n\n    pub fn now(&mut self) -> Duration {\n        *self.time.get_or_insert_with(get_monotonic_time)\n    }\n}\n\nimpl AdjustableClock {\n    pub fn new(mut inner: LazyClock) -> Self {\n        let time = inner.now();\n        Self {\n            inner,\n            current_time: time,\n            last_seen_time: time,\n            rate: 1.,\n            complete_instantly: false,\n        }\n    }\n\n    pub fn rate(&self) -> f64 {\n        self.rate\n    }\n\n    pub fn set_rate(&mut self, rate: f64) {\n        self.rate = rate.clamp(0., 1000.);\n    }\n\n    pub fn should_complete_instantly(&self) -> bool {\n        self.complete_instantly\n    }\n\n    pub fn set_complete_instantly(&mut self, value: bool) {\n        self.complete_instantly = value;\n    }\n\n    pub fn now(&mut self) -> Duration {\n        let time = self.inner.now();\n\n        if self.last_seen_time == time {\n            return self.current_time;\n        }\n\n        if self.last_seen_time < time {\n            let delta = time - self.last_seen_time;\n            let delta = delta.mul_f64(self.rate);\n            self.current_time = self.current_time.saturating_add(delta);\n        } else {\n            let delta = self.last_seen_time - time;\n            let delta = delta.mul_f64(self.rate);\n            self.current_time = self.current_time.saturating_sub(delta);\n        }\n\n        self.last_seen_time = time;\n        self.current_time\n    }\n}\n\nimpl Default for AdjustableClock {\n    fn default() -> Self {\n        Self::new(LazyClock::default())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn frozen_clock() {\n        let mut clock = Clock::with_time(Duration::ZERO);\n        assert_eq!(clock.now(), Duration::ZERO);\n\n        clock.set_unadjusted(Duration::from_millis(100));\n        assert_eq!(clock.now(), Duration::from_millis(100));\n\n        clock.set_unadjusted(Duration::from_millis(200));\n        assert_eq!(clock.now(), Duration::from_millis(200));\n    }\n\n    #[test]\n    fn rate_change() {\n        let mut clock = Clock::with_time(Duration::ZERO);\n        clock.set_rate(0.5);\n\n        clock.set_unadjusted(Duration::from_millis(100));\n        assert_eq!(clock.now_unadjusted(), Duration::from_millis(100));\n        assert_eq!(clock.now(), Duration::from_millis(50));\n\n        clock.set_unadjusted(Duration::from_millis(200));\n        assert_eq!(clock.now_unadjusted(), Duration::from_millis(200));\n        assert_eq!(clock.now(), Duration::from_millis(100));\n\n        clock.set_unadjusted(Duration::from_millis(150));\n        assert_eq!(clock.now_unadjusted(), Duration::from_millis(150));\n        assert_eq!(clock.now(), Duration::from_millis(75));\n\n        clock.set_rate(2.0);\n\n        clock.set_unadjusted(Duration::from_millis(250));\n        assert_eq!(clock.now_unadjusted(), Duration::from_millis(250));\n        assert_eq!(clock.now(), Duration::from_millis(275));\n    }\n}\n"
  },
  {
    "path": "src/animation/mod.rs",
    "content": "use std::time::Duration;\n\nuse keyframe::functions::{EaseOutCubic, EaseOutQuad};\nuse keyframe::EasingFunction;\n\nmod bezier;\nuse bezier::CubicBezier;\n\nmod spring;\npub use spring::{Spring, SpringParams};\n\nmod clock;\npub use clock::Clock;\n\n#[derive(Debug, Clone)]\npub struct Animation {\n    from: f64,\n    to: f64,\n    initial_velocity: f64,\n    is_off: bool,\n    duration: Duration,\n    /// Time until the animation first reaches `to`.\n    ///\n    /// Best effort; not always exactly precise.\n    clamped_duration: Duration,\n    start_time: Duration,\n    clock: Clock,\n    kind: Kind,\n}\n\n#[derive(Debug, Clone, Copy)]\nenum Kind {\n    Easing {\n        curve: Curve,\n    },\n    Spring(Spring),\n    Deceleration {\n        initial_velocity: f64,\n        deceleration_rate: f64,\n    },\n}\n\n#[derive(Debug, Clone, Copy)]\npub enum Curve {\n    Linear,\n    EaseOutQuad,\n    EaseOutCubic,\n    EaseOutExpo,\n    CubicBezier(CubicBezier),\n}\n\nimpl Animation {\n    pub fn new(\n        clock: Clock,\n        from: f64,\n        to: f64,\n        initial_velocity: f64,\n        config: niri_config::Animation,\n    ) -> Self {\n        // Scale the velocity by rate to keep the touchpad gestures feeling right.\n        let initial_velocity = initial_velocity / clock.rate().max(0.001);\n\n        let mut rv = Self::ease(clock, from, to, initial_velocity, 0, Curve::EaseOutCubic);\n        if config.off {\n            rv.is_off = true;\n            return rv;\n        }\n\n        rv.replace_config(config);\n        rv\n    }\n\n    pub fn replace_config(&mut self, config: niri_config::Animation) {\n        self.is_off = config.off;\n        if config.off {\n            self.duration = Duration::ZERO;\n            self.clamped_duration = Duration::ZERO;\n            return;\n        }\n\n        let start_time = self.start_time;\n\n        match config.kind {\n            niri_config::animations::Kind::Spring(p) => {\n                let params = SpringParams::new(p.damping_ratio, f64::from(p.stiffness), p.epsilon);\n\n                let spring = Spring {\n                    from: self.from,\n                    to: self.to,\n                    initial_velocity: self.initial_velocity,\n                    params,\n                };\n                *self = Self::spring(self.clock.clone(), spring);\n            }\n            niri_config::animations::Kind::Easing(p) => {\n                *self = Self::ease(\n                    self.clock.clone(),\n                    self.from,\n                    self.to,\n                    self.initial_velocity,\n                    u64::from(p.duration_ms),\n                    Curve::from(p.curve),\n                );\n            }\n        }\n\n        self.start_time = start_time;\n    }\n\n    /// Restarts the animation using the previous config.\n    pub fn restarted(&self, from: f64, to: f64, initial_velocity: f64) -> Self {\n        if self.is_off {\n            return self.clone();\n        }\n\n        // Scale the velocity by rate to keep the touchpad gestures feeling right.\n        let initial_velocity = initial_velocity / self.clock.rate().max(0.001);\n\n        match self.kind {\n            Kind::Easing { curve } => Self::ease(\n                self.clock.clone(),\n                from,\n                to,\n                initial_velocity,\n                self.duration.as_millis() as u64,\n                curve,\n            ),\n            Kind::Spring(spring) => {\n                let spring = Spring {\n                    from,\n                    to,\n                    initial_velocity: self.initial_velocity,\n                    params: spring.params,\n                };\n                Self::spring(self.clock.clone(), spring)\n            }\n            Kind::Deceleration {\n                initial_velocity,\n                deceleration_rate,\n            } => {\n                let threshold = 0.001; // FIXME\n                Self::decelerate(\n                    self.clock.clone(),\n                    from,\n                    initial_velocity,\n                    deceleration_rate,\n                    threshold,\n                )\n            }\n        }\n    }\n\n    pub fn ease(\n        clock: Clock,\n        from: f64,\n        to: f64,\n        initial_velocity: f64,\n        duration_ms: u64,\n        curve: Curve,\n    ) -> Self {\n        let duration = Duration::from_millis(duration_ms);\n        let kind = Kind::Easing { curve };\n\n        Self {\n            from,\n            to,\n            initial_velocity,\n            is_off: false,\n            duration,\n            // Our current curves never overshoot.\n            clamped_duration: duration,\n            start_time: clock.now(),\n            clock,\n            kind,\n        }\n    }\n\n    pub fn spring(clock: Clock, spring: Spring) -> Self {\n        let _span = tracy_client::span!(\"Animation::spring\");\n\n        let duration = spring.duration();\n        let clamped_duration = spring.clamped_duration().unwrap_or(duration);\n        let kind = Kind::Spring(spring);\n\n        Self {\n            from: spring.from,\n            to: spring.to,\n            initial_velocity: spring.initial_velocity,\n            is_off: false,\n            duration,\n            clamped_duration,\n            start_time: clock.now(),\n            clock,\n            kind,\n        }\n    }\n\n    pub fn decelerate(\n        clock: Clock,\n        from: f64,\n        initial_velocity: f64,\n        deceleration_rate: f64,\n        threshold: f64,\n    ) -> Self {\n        let duration_s = if initial_velocity == 0. {\n            0.\n        } else {\n            let coeff = 1000. * deceleration_rate.ln();\n            (-coeff * threshold / initial_velocity.abs()).ln() / coeff\n        };\n        let duration = Duration::from_secs_f64(duration_s);\n\n        let to = from - initial_velocity / (1000. * deceleration_rate.ln());\n\n        let kind = Kind::Deceleration {\n            initial_velocity,\n            deceleration_rate,\n        };\n\n        Self {\n            from,\n            to,\n            initial_velocity,\n            is_off: false,\n            duration,\n            clamped_duration: duration,\n            start_time: clock.now(),\n            clock,\n            kind,\n        }\n    }\n\n    pub fn is_done(&self) -> bool {\n        if self.clock.should_complete_instantly() {\n            return true;\n        }\n\n        self.clock.now() >= self.start_time + self.duration\n    }\n\n    pub fn is_clamped_done(&self) -> bool {\n        if self.clock.should_complete_instantly() {\n            return true;\n        }\n\n        self.clock.now() >= self.start_time + self.clamped_duration\n    }\n\n    pub fn value_at(&self, at: Duration) -> f64 {\n        if at <= self.start_time {\n            // Return from when at == start_time so that when the animations are off, the behavior\n            // within a single event loop cycle (i.e. no time had passed since the start of an\n            // animation) matches the behavior when the animations are on.\n            return self.from;\n        } else if self.start_time + self.duration <= at {\n            return self.to;\n        }\n\n        if self.clock.should_complete_instantly() {\n            return self.to;\n        }\n\n        let passed = at.saturating_sub(self.start_time);\n\n        match self.kind {\n            Kind::Easing { curve } => {\n                let passed = passed.as_secs_f64();\n                let total = self.duration.as_secs_f64();\n                let x = (passed / total).clamp(0., 1.);\n                curve.y(x) * (self.to - self.from) + self.from\n            }\n            Kind::Spring(spring) => {\n                let value = spring.value_at(passed);\n\n                // Protect against numerical instability.\n                let range = (self.to - self.from) * 10.;\n                let a = self.from - range;\n                let b = self.to + range;\n                if self.from <= self.to {\n                    value.clamp(a, b)\n                } else {\n                    value.clamp(b, a)\n                }\n            }\n            Kind::Deceleration {\n                initial_velocity,\n                deceleration_rate,\n            } => {\n                let passed = passed.as_secs_f64();\n                let coeff = 1000. * deceleration_rate.ln();\n                self.from + (deceleration_rate.powf(1000. * passed) - 1.) / coeff * initial_velocity\n            }\n        }\n    }\n\n    pub fn value(&self) -> f64 {\n        self.value_at(self.clock.now())\n    }\n\n    /// Returns a value that stops at the target value after first reaching it.\n    ///\n    /// Best effort; not always exactly precise.\n    pub fn clamped_value(&self) -> f64 {\n        if self.is_clamped_done() {\n            return self.to;\n        }\n\n        self.value()\n    }\n\n    pub fn to(&self) -> f64 {\n        self.to\n    }\n\n    pub fn from(&self) -> f64 {\n        self.from\n    }\n\n    pub fn start_time(&self) -> Duration {\n        self.start_time\n    }\n\n    pub fn end_time(&self) -> Duration {\n        self.start_time + self.duration\n    }\n\n    pub fn duration(&self) -> Duration {\n        self.duration\n    }\n\n    pub fn offset(&mut self, offset: f64) {\n        self.from += offset;\n        self.to += offset;\n\n        if let Kind::Spring(spring) = &mut self.kind {\n            spring.from += offset;\n            spring.to += offset;\n        }\n    }\n}\n\nimpl Curve {\n    pub fn y(self, x: f64) -> f64 {\n        match self {\n            Curve::Linear => x,\n            Curve::EaseOutQuad => EaseOutQuad.y(x),\n            Curve::EaseOutCubic => EaseOutCubic.y(x),\n            Curve::EaseOutExpo => 1. - 2f64.powf(-10. * x),\n            Curve::CubicBezier(b) => b.y(x),\n        }\n    }\n}\n\nimpl From<niri_config::animations::Curve> for Curve {\n    fn from(value: niri_config::animations::Curve) -> Self {\n        match value {\n            niri_config::animations::Curve::Linear => Curve::Linear,\n            niri_config::animations::Curve::EaseOutQuad => Curve::EaseOutQuad,\n            niri_config::animations::Curve::EaseOutCubic => Curve::EaseOutCubic,\n            niri_config::animations::Curve::EaseOutExpo => Curve::EaseOutExpo,\n            niri_config::animations::Curve::CubicBezier(x1, y1, x2, y2) => {\n                Curve::CubicBezier(CubicBezier::new(x1, y1, x2, y2))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/animation/spring.rs",
    "content": "use std::time::Duration;\n\n#[derive(Debug, Clone, Copy)]\npub struct SpringParams {\n    pub damping: f64,\n    pub mass: f64,\n    pub stiffness: f64,\n    pub epsilon: f64,\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct Spring {\n    pub from: f64,\n    pub to: f64,\n    pub initial_velocity: f64,\n    pub params: SpringParams,\n}\n\nimpl SpringParams {\n    pub fn new(damping_ratio: f64, stiffness: f64, epsilon: f64) -> Self {\n        let damping_ratio = damping_ratio.max(0.);\n        let stiffness = stiffness.max(0.);\n        let epsilon = epsilon.max(0.);\n\n        let mass = 1.;\n        let critical_damping = 2. * (mass * stiffness).sqrt();\n        let damping = damping_ratio * critical_damping;\n\n        Self {\n            damping,\n            mass,\n            stiffness,\n            epsilon,\n        }\n    }\n}\n\nimpl Spring {\n    pub fn value_at(&self, t: Duration) -> f64 {\n        self.oscillate(t.as_secs_f64())\n    }\n\n    // Based on libadwaita (LGPL-2.1-or-later):\n    // https://gitlab.gnome.org/GNOME/libadwaita/-/blob/1.4.4/src/adw-spring-animation.c,\n    // which itself is based on (MIT):\n    // https://github.com/robb/RBBAnimation/blob/master/RBBAnimation/RBBSpringAnimation.m\n    /// Computes and returns the duration until the spring is at rest.\n    pub fn duration(&self) -> Duration {\n        const DELTA: f64 = 0.001;\n\n        let beta = self.params.damping / (2. * self.params.mass);\n\n        if beta.abs() <= f64::EPSILON || beta < 0. {\n            return Duration::MAX;\n        }\n\n        if (self.to - self.from).abs() <= f64::EPSILON {\n            return Duration::ZERO;\n        }\n\n        let omega0 = (self.params.stiffness / self.params.mass).sqrt();\n\n        // As first ansatz for the overdamped solution,\n        // and general estimation for the oscillating ones\n        // we take the value of the envelope when it's < epsilon.\n        let mut x0 = -self.params.epsilon.ln() / beta;\n\n        // f64::EPSILON is too small for this specific comparison, so we use\n        // f32::EPSILON even though it's doubles.\n        if (beta - omega0).abs() <= f64::from(f32::EPSILON) || beta < omega0 {\n            return Duration::from_secs_f64(x0);\n        }\n\n        // Since the overdamped solution decays way slower than the envelope\n        // we need to use the value of the oscillation itself.\n        // Newton's root finding method is a good candidate in this particular case:\n        // https://en.wikipedia.org/wiki/Newton%27s_method\n        let mut y0 = self.oscillate(x0);\n        let m = (self.oscillate(x0 + DELTA) - y0) / DELTA;\n\n        let mut x1 = (self.to - y0 + m * x0) / m;\n        let mut y1 = self.oscillate(x1);\n\n        let mut i = 0;\n        while (self.to - y1).abs() > self.params.epsilon {\n            if i > 1000 {\n                return Duration::ZERO;\n            }\n\n            x0 = x1;\n            y0 = y1;\n\n            let m = (self.oscillate(x0 + DELTA) - y0) / DELTA;\n\n            x1 = (self.to - y0 + m * x0) / m;\n            y1 = self.oscillate(x1);\n\n            // Overdamped springs have some numerical stability issues...\n            if !y1.is_finite() {\n                return Duration::from_secs_f64(x0);\n            }\n\n            i += 1;\n        }\n\n        Duration::from_secs_f64(x1)\n    }\n\n    /// Computes and returns the duration until the spring reaches its target position.\n    pub fn clamped_duration(&self) -> Option<Duration> {\n        let beta = self.params.damping / (2. * self.params.mass);\n\n        if beta.abs() <= f64::EPSILON || beta < 0. {\n            return Some(Duration::MAX);\n        }\n\n        if (self.to - self.from).abs() <= f64::EPSILON {\n            return Some(Duration::ZERO);\n        }\n\n        // The first frame is not that important and we avoid finding the trivial 0 for in-place\n        // animations.\n        let mut i = 1u16;\n        let mut y = self.oscillate(f64::from(i) / 1000.);\n\n        while (self.to - self.from > f64::EPSILON && self.to - y > self.params.epsilon)\n            || (self.from - self.to > f64::EPSILON && y - self.to > self.params.epsilon)\n        {\n            if i > 3000 {\n                return None;\n            }\n\n            i += 1;\n            y = self.oscillate(f64::from(i) / 1000.);\n        }\n\n        Some(Duration::from_millis(u64::from(i)))\n    }\n\n    /// Returns the spring position at a given time in seconds.\n    fn oscillate(&self, t: f64) -> f64 {\n        let b = self.params.damping;\n        let m = self.params.mass;\n        let k = self.params.stiffness;\n        let v0 = self.initial_velocity;\n\n        let beta = b / (2. * m);\n        let omega0 = (k / m).sqrt();\n\n        let x0 = self.from - self.to;\n\n        let envelope = (-beta * t).exp();\n\n        // Solutions of the form C1*e^(lambda1*x) + C2*e^(lambda2*x)\n        // for the differential equation m*ẍ+b*ẋ+kx = 0\n\n        // f64::EPSILON is too small for this specific comparison, so we use\n        // f32::EPSILON even though it's doubles.\n        if (beta - omega0).abs() <= f64::from(f32::EPSILON) {\n            // Critically damped.\n            self.to + envelope * (x0 + (beta * x0 + v0) * t)\n        } else if beta < omega0 {\n            // Underdamped.\n            let omega1 = ((omega0 * omega0) - (beta * beta)).sqrt();\n\n            self.to\n                + envelope\n                    * (x0 * (omega1 * t).cos() + ((beta * x0 + v0) / omega1) * (omega1 * t).sin())\n        } else {\n            // Overdamped.\n            let omega2 = ((beta * beta) - (omega0 * omega0)).sqrt();\n\n            self.to\n                + envelope\n                    * (x0 * (omega2 * t).cosh() + ((beta * x0 + v0) / omega2) * (omega2 * t).sinh())\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn overdamped_spring_equal_from_to_nan() {\n        let spring = Spring {\n            from: 0.,\n            to: 0.,\n            initial_velocity: 0.,\n            params: SpringParams::new(1.15, 850., 0.0001),\n        };\n        let _ = spring.duration();\n        let _ = spring.clamped_duration();\n        let _ = spring.value_at(Duration::ZERO);\n    }\n\n    #[test]\n    fn overdamped_spring_duration_panic() {\n        let spring = Spring {\n            from: 0.,\n            to: 1.,\n            initial_velocity: 0.,\n            params: SpringParams::new(6., 1200., 0.0001),\n        };\n        let _ = spring.duration();\n        let _ = spring.clamped_duration();\n        let _ = spring.value_at(Duration::ZERO);\n    }\n}\n"
  },
  {
    "path": "src/backend/headless.rs",
    "content": "//! Headless backend for tests.\n//!\n//! This can eventually grow into a more complete backend if needed, but for now it's missing some\n//! crucial parts like dmabufs.\n\nuse std::mem;\nuse std::sync::{Arc, Mutex};\n\nuse anyhow::Context as _;\nuse niri_config::OutputName;\nuse smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::egl::native::EGLSurfacelessDisplay;\nuse smithay::backend::egl::{EGLContext, EGLDisplay};\nuse smithay::backend::renderer::element::RenderElementStates;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::output::{Mode, Output, PhysicalProperties, Subpixel};\nuse smithay::reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback;\nuse smithay::utils::Size;\nuse smithay::wayland::presentation::Refresh;\n\nuse super::{IpcOutputMap, OutputId, RenderResult};\nuse crate::niri::{Niri, RedrawState};\nuse crate::render_helpers::{resources, shaders};\nuse crate::utils::{get_monotonic_time, logical_output};\n\npub struct Headless {\n    renderer: Option<GlesRenderer>,\n    ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n}\n\nimpl Headless {\n    pub fn new() -> Self {\n        Self {\n            renderer: None,\n            ipc_outputs: Default::default(),\n        }\n    }\n\n    pub fn init(&mut self, _niri: &mut Niri) {}\n\n    pub fn add_renderer(&mut self) -> anyhow::Result<()> {\n        if self.renderer.is_some() {\n            error!(\"add_renderer: renderer must not already exist\");\n            return Ok(());\n        }\n\n        let mut renderer = unsafe {\n            let display =\n                EGLDisplay::new(EGLSurfacelessDisplay).context(\"error creating EGL display\")?;\n            let context = EGLContext::new(&display).context(\"error creating EGL context\")?;\n            GlesRenderer::new(context).context(\"error creating renderer\")?\n        };\n\n        resources::init(&mut renderer);\n        shaders::init(&mut renderer);\n\n        self.renderer = Some(renderer);\n        Ok(())\n    }\n\n    pub fn add_output(&mut self, niri: &mut Niri, n: u8, size: (u16, u16)) {\n        let connector = format!(\"headless-{n}\");\n        let make = \"niri\".to_string();\n        let model = \"headless\".to_string();\n        let serial = n.to_string();\n\n        let output = Output::new(\n            connector.clone(),\n            PhysicalProperties {\n                size: (0, 0).into(),\n                subpixel: Subpixel::Unknown,\n                make: make.clone(),\n                model: model.clone(),\n                serial_number: serial.clone(),\n            },\n        );\n\n        let mode = Mode {\n            size: Size::from((i32::from(size.0), i32::from(size.1))),\n            refresh: 60_000,\n        };\n        output.change_current_state(Some(mode), None, None, None);\n        output.set_preferred(mode);\n\n        output.user_data().insert_if_missing(|| OutputName {\n            connector,\n            make: Some(make),\n            model: Some(model),\n            serial: Some(serial),\n        });\n\n        let physical_properties = output.physical_properties();\n        self.ipc_outputs.lock().unwrap().insert(\n            OutputId::next(),\n            niri_ipc::Output {\n                name: output.name(),\n                make: physical_properties.make,\n                model: physical_properties.model,\n                serial: None,\n                physical_size: None,\n                modes: vec![niri_ipc::Mode {\n                    width: size.0,\n                    height: size.1,\n                    refresh_rate: 60_000,\n                    is_preferred: true,\n                }],\n                current_mode: Some(0),\n                is_custom_mode: true,\n                vrr_supported: false,\n                vrr_enabled: false,\n                logical: Some(logical_output(&output)),\n            },\n        );\n\n        niri.add_output(output, None, false);\n    }\n\n    pub fn seat_name(&self) -> String {\n        \"headless\".to_owned()\n    }\n\n    pub fn with_primary_renderer<T>(\n        &mut self,\n        f: impl FnOnce(&mut GlesRenderer) -> T,\n    ) -> Option<T> {\n        self.renderer.as_mut().map(f)\n    }\n\n    pub fn render(&mut self, niri: &mut Niri, output: &Output) -> RenderResult {\n        let states = RenderElementStates::default();\n        let mut presentation_feedbacks = niri.take_presentation_feedbacks(output, &states);\n        presentation_feedbacks.presented::<_, smithay::utils::Monotonic>(\n            get_monotonic_time(),\n            Refresh::Unknown,\n            0,\n            wp_presentation_feedback::Kind::empty(),\n        );\n\n        let output_state = niri.output_state.get_mut(output).unwrap();\n        match mem::replace(&mut output_state.redraw_state, RedrawState::Idle) {\n            RedrawState::Idle => unreachable!(),\n            RedrawState::Queued => (),\n            RedrawState::WaitingForVBlank { .. } => unreachable!(),\n            RedrawState::WaitingForEstimatedVBlank(_) => unreachable!(),\n            RedrawState::WaitingForEstimatedVBlankAndQueued(_) => unreachable!(),\n        }\n\n        output_state.frame_callback_sequence = output_state.frame_callback_sequence.wrapping_add(1);\n\n        // FIXME: request redraw on unfinished animations remain\n\n        RenderResult::Submitted\n    }\n\n    pub fn import_dmabuf(&mut self, _dmabuf: &Dmabuf) -> bool {\n        unimplemented!()\n    }\n\n    pub fn ipc_outputs(&self) -> Arc<Mutex<IpcOutputMap>> {\n        self.ipc_outputs.clone()\n    }\n}\n\nimpl Default for Headless {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "src/backend/mod.rs",
    "content": "use std::collections::HashMap;\nuse std::sync::{Arc, Mutex};\nuse std::time::Duration;\n\nuse niri_config::{Config, ModKey};\nuse smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::output::Output;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\n\nuse crate::niri::Niri;\nuse crate::utils::id::IdCounter;\n\npub mod tty;\npub use tty::Tty;\n\npub mod winit;\npub use winit::Winit;\n\npub mod headless;\npub use headless::Headless;\n\n#[allow(clippy::large_enum_variant)]\npub enum Backend {\n    Tty(Tty),\n    Winit(Winit),\n    Headless(Headless),\n}\n\n#[derive(PartialEq, Eq)]\npub enum RenderResult {\n    /// The frame was submitted to the backend for presentation.\n    Submitted,\n    /// Rendering succeeded, but there was no damage.\n    NoDamage,\n    /// The frame was not rendered and submitted, due to an error or otherwise.\n    Skipped,\n}\n\npub type IpcOutputMap = HashMap<OutputId, niri_ipc::Output>;\n\nstatic OUTPUT_ID_COUNTER: IdCounter = IdCounter::new();\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct OutputId(u64);\n\nimpl OutputId {\n    fn next() -> OutputId {\n        OutputId(OUTPUT_ID_COUNTER.next())\n    }\n\n    pub fn get(self) -> u64 {\n        self.0\n    }\n}\n\nimpl Backend {\n    pub fn init(&mut self, niri: &mut Niri) {\n        let _span = tracy_client::span!(\"Backend::init\");\n        match self {\n            Backend::Tty(tty) => tty.init(niri),\n            Backend::Winit(winit) => winit.init(niri),\n            Backend::Headless(headless) => headless.init(niri),\n        }\n    }\n\n    pub fn seat_name(&self) -> String {\n        match self {\n            Backend::Tty(tty) => tty.seat_name(),\n            Backend::Winit(winit) => winit.seat_name(),\n            Backend::Headless(headless) => headless.seat_name(),\n        }\n    }\n\n    pub fn with_primary_renderer<T>(\n        &mut self,\n        f: impl FnOnce(&mut GlesRenderer) -> T,\n    ) -> Option<T> {\n        match self {\n            Backend::Tty(tty) => tty.with_primary_renderer(f),\n            Backend::Winit(winit) => winit.with_primary_renderer(f),\n            Backend::Headless(headless) => headless.with_primary_renderer(f),\n        }\n    }\n\n    pub fn render(\n        &mut self,\n        niri: &mut Niri,\n        output: &Output,\n        target_presentation_time: Duration,\n    ) -> RenderResult {\n        match self {\n            Backend::Tty(tty) => tty.render(niri, output, target_presentation_time),\n            Backend::Winit(winit) => winit.render(niri, output),\n            Backend::Headless(headless) => headless.render(niri, output),\n        }\n    }\n\n    pub fn mod_key(&self, config: &Config) -> ModKey {\n        match self {\n            Backend::Winit(_) => config.input.mod_key_nested.unwrap_or({\n                if let Some(ModKey::Alt) = config.input.mod_key {\n                    ModKey::Super\n                } else {\n                    ModKey::Alt\n                }\n            }),\n            Backend::Tty(_) | Backend::Headless(_) => config.input.mod_key.unwrap_or(ModKey::Super),\n        }\n    }\n\n    pub fn change_vt(&mut self, vt: i32) {\n        match self {\n            Backend::Tty(tty) => tty.change_vt(vt),\n            Backend::Winit(_) => (),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn suspend(&mut self) {\n        match self {\n            Backend::Tty(tty) => tty.suspend(),\n            Backend::Winit(_) => (),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn toggle_debug_tint(&mut self) {\n        match self {\n            Backend::Tty(tty) => tty.toggle_debug_tint(),\n            Backend::Winit(winit) => winit.toggle_debug_tint(),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn import_dmabuf(&mut self, dmabuf: &Dmabuf) -> bool {\n        match self {\n            Backend::Tty(tty) => tty.import_dmabuf(dmabuf),\n            Backend::Winit(winit) => winit.import_dmabuf(dmabuf),\n            Backend::Headless(headless) => headless.import_dmabuf(dmabuf),\n        }\n    }\n\n    pub fn early_import(&mut self, surface: &WlSurface) {\n        match self {\n            Backend::Tty(tty) => tty.early_import(surface),\n            Backend::Winit(_) => (),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn ipc_outputs(&self) -> Arc<Mutex<IpcOutputMap>> {\n        match self {\n            Backend::Tty(tty) => tty.ipc_outputs(),\n            Backend::Winit(winit) => winit.ipc_outputs(),\n            Backend::Headless(headless) => headless.ipc_outputs(),\n        }\n    }\n\n    #[cfg(feature = \"xdp-gnome-screencast\")]\n    pub fn gbm_device(\n        &self,\n    ) -> Option<smithay::backend::allocator::gbm::GbmDevice<smithay::backend::drm::DrmDeviceFd>>\n    {\n        match self {\n            Backend::Tty(tty) => tty.primary_gbm_device(),\n            Backend::Winit(_) => None,\n            Backend::Headless(_) => None,\n        }\n    }\n\n    pub fn set_monitors_active(&mut self, active: bool) {\n        match self {\n            Backend::Tty(tty) => tty.set_monitors_active(active),\n            Backend::Winit(_) => (),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn set_output_on_demand_vrr(&mut self, niri: &mut Niri, output: &Output, enable_vrr: bool) {\n        match self {\n            Backend::Tty(tty) => tty.set_output_on_demand_vrr(niri, output, enable_vrr),\n            Backend::Winit(_) => (),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn update_ignored_nodes_config(&mut self, niri: &mut Niri) {\n        match self {\n            Backend::Tty(tty) => tty.update_ignored_nodes_config(niri),\n            Backend::Winit(_) => (),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn on_output_config_changed(&mut self, niri: &mut Niri) {\n        match self {\n            Backend::Tty(tty) => tty.on_output_config_changed(niri),\n            Backend::Winit(_) => (),\n            Backend::Headless(_) => (),\n        }\n    }\n\n    pub fn tty_checked(&mut self) -> Option<&mut Tty> {\n        if let Self::Tty(v) = self {\n            Some(v)\n        } else {\n            None\n        }\n    }\n\n    pub fn tty(&mut self) -> &mut Tty {\n        if let Self::Tty(v) = self {\n            v\n        } else {\n            panic!(\"backend is not Tty\");\n        }\n    }\n\n    pub fn winit(&mut self) -> &mut Winit {\n        if let Self::Winit(v) = self {\n            v\n        } else {\n            panic!(\"backend is not Winit\")\n        }\n    }\n\n    pub fn headless(&mut self) -> &mut Headless {\n        if let Self::Headless(v) = self {\n            v\n        } else {\n            panic!(\"backend is not Headless\")\n        }\n    }\n}\n"
  },
  {
    "path": "src/backend/tty.rs",
    "content": "use std::cell::RefCell;\nuse std::collections::{HashMap, HashSet};\nuse std::fmt::Write;\nuse std::iter::zip;\nuse std::num::NonZeroU64;\nuse std::os::fd::{AsFd, OwnedFd};\nuse std::path::Path;\nuse std::rc::Rc;\nuse std::sync::{Arc, Mutex};\nuse std::time::Duration;\nuse std::{io, mem};\n\nuse anyhow::{anyhow, bail, ensure, Context};\nuse bytemuck::cast_slice_mut;\nuse drm_ffi::drm_mode_modeinfo;\nuse libc::dev_t;\nuse niri_config::output::Modeline;\nuse niri_config::{Config, OutputName};\nuse niri_ipc::{HSyncPolarity, VSyncPolarity};\nuse smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::allocator::format::FormatSet;\nuse smithay::backend::allocator::gbm::{GbmAllocator, GbmBufferFlags, GbmDevice};\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::drm::compositor::{DrmCompositor, FrameFlags, PrimaryPlaneElement};\nuse smithay::backend::drm::exporter::gbm::GbmFramebufferExporter;\nuse smithay::backend::drm::{\n    DrmDevice, DrmDeviceFd, DrmEvent, DrmEventMetadata, DrmEventTime, DrmNode, NodeType, VrrSupport,\n};\nuse smithay::backend::egl::context::ContextPriority;\nuse smithay::backend::egl::{EGLDevice, EGLDisplay};\nuse smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface};\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::backend::renderer::multigpu::gbm::GbmGlesBackend;\nuse smithay::backend::renderer::multigpu::{GpuManager, MultiFrame, MultiRenderer};\nuse smithay::backend::renderer::{DebugFlags, ImportDma, ImportEgl, RendererSuper};\nuse smithay::backend::session::libseat::LibSeatSession;\nuse smithay::backend::session::{Event as SessionEvent, Session};\nuse smithay::backend::udev::{self, UdevBackend, UdevEvent};\nuse smithay::desktop::utils::OutputPresentationFeedback;\nuse smithay::output::{Mode, Output, OutputModeSource, PhysicalProperties};\nuse smithay::reexports::calloop::timer::{TimeoutAction, Timer};\nuse smithay::reexports::calloop::{Dispatcher, LoopHandle, RegistrationToken};\nuse smithay::reexports::drm::control::atomic::AtomicModeReq;\nuse smithay::reexports::drm::control::dumbbuffer::DumbBuffer;\nuse smithay::reexports::drm::control::{\n    self, connector, crtc, plane, property, AtomicCommitFlags, Device, Mode as DrmMode, ModeFlags,\n    ModeTypeFlags, PlaneType, ResourceHandle,\n};\nuse smithay::reexports::gbm::Modifier;\nuse smithay::reexports::input::Libinput;\nuse smithay::reexports::rustix::fs::OFlags;\nuse smithay::reexports::wayland_protocols;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::utils::{DeviceFd, Transform};\nuse smithay::wayland::dmabuf::{DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal};\nuse smithay::wayland::drm_lease::{\n    DrmLease, DrmLeaseBuilder, DrmLeaseRequest, DrmLeaseState, LeaseRejected,\n};\nuse smithay::wayland::presentation::Refresh;\nuse smithay_drm_extras::drm_scanner::{DrmScanEvent, DrmScanner};\nuse wayland_protocols::wp::linux_dmabuf::zv1::server::zwp_linux_dmabuf_feedback_v1::TrancheFlags;\nuse wayland_protocols::wp::presentation_time::server::wp_presentation_feedback;\n\nuse super::{IpcOutputMap, RenderResult};\nuse crate::backend::OutputId;\nuse crate::frame_clock::FrameClock;\nuse crate::niri::{Niri, RedrawState, State};\nuse crate::render_helpers::debug::draw_damage;\nuse crate::render_helpers::renderer::AsGlesRenderer;\nuse crate::render_helpers::{resources, shaders, RenderTarget};\nuse crate::utils::{get_monotonic_time, is_laptop_panel, logical_output, PanelOrientation};\n\nconst SUPPORTED_COLOR_FORMATS: [Fourcc; 4] = [\n    Fourcc::Xrgb8888,\n    Fourcc::Xbgr8888,\n    Fourcc::Argb8888,\n    Fourcc::Abgr8888,\n];\n\npub struct Tty {\n    config: Rc<RefCell<Config>>,\n    session: LibSeatSession,\n    udev_dispatcher: Dispatcher<'static, UdevBackend, State>,\n    libinput: Libinput,\n    gpu_manager: GpuManager<GbmGlesBackend<GlesRenderer, DrmDeviceFd>>,\n    // DRM node corresponding to the primary GPU. May or may not be the same as\n    // primary_render_node.\n    primary_node: DrmNode,\n    // DRM render node corresponding to the primary GPU.\n    primary_render_node: DrmNode,\n    // Ignored DRM nodes.\n    ignored_nodes: HashSet<DrmNode>,\n    // Devices indexed by DRM node (not necessarily the render node).\n    devices: HashMap<DrmNode, OutputDevice>,\n    // The dma-buf global corresponds to the output device (the primary GPU). It is only `Some()`\n    // if we have a device corresponding to the primary GPU.\n    dmabuf_global: Option<DmabufGlobal>,\n    // The output config had changed, but the session is paused, so we need to update it on resume.\n    update_output_config_on_resume: bool,\n    // The ignored nodes have changed, but the session is paused, so we need to update it on\n    // resume.\n    update_ignored_nodes_on_resume: bool,\n    // Whether the debug tinting is enabled.\n    debug_tint: bool,\n    ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n}\n\npub type TtyRenderer<'render> = MultiRenderer<\n    'render,\n    'render,\n    GbmGlesBackend<GlesRenderer, DrmDeviceFd>,\n    GbmGlesBackend<GlesRenderer, DrmDeviceFd>,\n>;\n\npub type TtyFrame<'render, 'frame, 'buffer> = MultiFrame<\n    'render,\n    'render,\n    'frame,\n    'buffer,\n    GbmGlesBackend<GlesRenderer, DrmDeviceFd>,\n    GbmGlesBackend<GlesRenderer, DrmDeviceFd>,\n>;\n\npub type TtyRendererError<'render> = <TtyRenderer<'render> as RendererSuper>::Error;\n\ntype GbmDrmCompositor = DrmCompositor<\n    GbmAllocator<DrmDeviceFd>,\n    GbmFramebufferExporter<DrmDeviceFd>,\n    (OutputPresentationFeedback, Duration),\n    DrmDeviceFd,\n>;\n\npub struct OutputDevice {\n    token: RegistrationToken,\n    // Can be None for display-only devices such as DisplayLink.\n    render_node: Option<DrmNode>,\n    drm_scanner: DrmScanner,\n    surfaces: HashMap<crtc::Handle, Surface>,\n    known_crtcs: HashMap<crtc::Handle, CrtcInfo>,\n    // SAFETY: drop after all the objects used with them are dropped.\n    // See https://github.com/Smithay/smithay/issues/1102.\n    drm: DrmDevice,\n    gbm: GbmDevice<DrmDeviceFd>,\n    // For display-only devices this will be the allocator from the primary device.\n    allocator: GbmAllocator<DrmDeviceFd>,\n\n    pub drm_lease_state: Option<DrmLeaseState>,\n    non_desktop_connectors: HashSet<(connector::Handle, crtc::Handle)>,\n    active_leases: Vec<DrmLease>,\n}\n\n// A connected, but not necessarily enabled, crtc.\n#[derive(Debug, Clone)]\npub struct CrtcInfo {\n    id: OutputId,\n    name: OutputName,\n}\n\nimpl OutputDevice {\n    pub fn lease_request(\n        &self,\n        request: DrmLeaseRequest,\n    ) -> Result<DrmLeaseBuilder, LeaseRejected> {\n        let mut builder = DrmLeaseBuilder::new(&self.drm);\n        for connector in request.connectors {\n            let (_, crtc) = self\n                .non_desktop_connectors\n                .iter()\n                .find(|(conn, _)| connector == *conn)\n                .ok_or_else(|| {\n                    warn!(\"Attempted to lease connector that is not non-desktop\");\n                    LeaseRejected::default()\n                })?;\n            builder.add_connector(connector);\n            builder.add_crtc(*crtc);\n            let planes = self.drm.planes(crtc).map_err(LeaseRejected::with_cause)?;\n            let (primary_plane, primary_plane_claim) = planes\n                .primary\n                .iter()\n                .find_map(|plane| {\n                    self.drm\n                        .claim_plane(plane.handle, *crtc)\n                        .map(|claim| (plane, claim))\n                })\n                .ok_or_else(LeaseRejected::default)?;\n            builder.add_plane(primary_plane.handle, primary_plane_claim);\n        }\n        Ok(builder)\n    }\n\n    pub fn new_lease(&mut self, lease: DrmLease) {\n        self.active_leases.push(lease);\n    }\n\n    pub fn remove_lease(&mut self, lease_id: u32) {\n        self.active_leases.retain(|l| l.id() != lease_id);\n    }\n\n    pub fn known_crtc_name(\n        &self,\n        crtc: &crtc::Handle,\n        conn: &connector::Info,\n        disable_monitor_names: bool,\n    ) -> OutputName {\n        if disable_monitor_names {\n            let conn_name = format_connector_name(conn);\n            return OutputName {\n                connector: conn_name,\n                make: None,\n                model: None,\n                serial: None,\n            };\n        }\n\n        let Some(info) = self.known_crtcs.get(crtc) else {\n            let conn_name = format_connector_name(conn);\n            error!(\"crtc for connector {conn_name} missing from known\");\n            return OutputName {\n                connector: conn_name,\n                make: None,\n                model: None,\n                serial: None,\n            };\n        };\n        info.name.clone()\n    }\n\n    fn cleanup_mismatching_resources(\n        &self,\n        should_be_off: &dyn Fn(crtc::Handle, &connector::Info) -> bool,\n    ) -> anyhow::Result<()> {\n        let _span = tracy_client::span!(\"OutputDevice::cleanup_disconnected_resources\");\n\n        let res_handles = self\n            .drm\n            .resource_handles()\n            .context(\"error getting plane handles\")?;\n        let plane_handles = self\n            .drm\n            .plane_handles()\n            .context(\"error getting plane handles\")?;\n\n        let mut req = AtomicModeReq::new();\n\n        // We want to disable all CRTCs that do not correspond to a connector we're using.\n        let mut cleanup = HashSet::<crtc::Handle>::new();\n        cleanup.extend(res_handles.crtcs());\n\n        for (conn, info) in self.drm_scanner.connectors() {\n            // We only keep the connector if it has a CRTC and the output isn't off in niri.\n            if let Some(crtc) = self.drm_scanner.crtc_for_connector(conn) {\n                // Verify that the connector's current CRTC matches the CRTC we expect. If not,\n                // clear the CRTC and the connector so that all connectors can get the expected\n                // CRTCs afterwards. (We do this because we do not handle CRTC rotations across TTY\n                // switches.)\n                let mut has_different_crtc = false;\n                if let Some(enc) = info.current_encoder() {\n                    match self.drm.get_encoder(enc) {\n                        Ok(enc) => {\n                            if let Some(current_crtc) = enc.crtc() {\n                                if current_crtc != crtc {\n                                    has_different_crtc = true;\n                                }\n                            }\n                        }\n                        Err(err) => {\n                            debug!(\"couldn't get encoder: {err:?}\");\n                            // Err on the safe side.\n                            has_different_crtc = true;\n                        }\n                    }\n                }\n\n                if !has_different_crtc && !should_be_off(crtc, info) {\n                    // Keep the corresponding CRTC.\n                    cleanup.remove(&crtc);\n                    continue;\n                }\n            }\n\n            // Clear the connector.\n            let Some((crtc_id, _, _)) = find_drm_property(&self.drm, *conn, \"CRTC_ID\") else {\n                debug!(\"couldn't find connector CRTC_ID property\");\n                continue;\n            };\n\n            req.add_property(*conn, crtc_id, property::Value::CRTC(None));\n        }\n\n        // Legacy fallback.\n        if !self.drm.is_atomic() {\n            for crtc in res_handles.crtcs() {\n                #[allow(deprecated)]\n                let _ = self.drm.set_cursor(*crtc, Option::<&DumbBuffer>::None);\n            }\n            for crtc in cleanup {\n                let _ = self.drm.set_crtc(crtc, None, (0, 0), &[], None);\n            }\n            return Ok(());\n        }\n\n        // Disable non-primary planes, and planes belonging to disabled CRTCs.\n        let is_primary = |plane: plane::Handle| {\n            if let Some((_, info, value)) = find_drm_property(&self.drm, plane, \"type\") {\n                match info.value_type().convert_value(value) {\n                    property::Value::Enum(Some(val)) => val.value() == PlaneType::Primary as u64,\n                    _ => false,\n                }\n            } else {\n                debug!(\"couldn't find plane type property\");\n                false\n            }\n        };\n\n        for plane in plane_handles {\n            let info = match self.drm.get_plane(plane) {\n                Ok(x) => x,\n                Err(err) => {\n                    debug!(\"error getting plane: {err:?}\");\n                    continue;\n                }\n            };\n\n            let Some(crtc) = info.crtc() else {\n                continue;\n            };\n\n            if !cleanup.contains(&crtc) && is_primary(plane) {\n                continue;\n            }\n\n            let Some((crtc_id, _, _)) = find_drm_property(&self.drm, plane, \"CRTC_ID\") else {\n                debug!(\"couldn't find plane CRTC_ID property\");\n                continue;\n            };\n\n            let Some((fb_id, _, _)) = find_drm_property(&self.drm, plane, \"FB_ID\") else {\n                debug!(\"couldn't find plane FB_ID property\");\n                continue;\n            };\n\n            req.add_property(plane, crtc_id, property::Value::CRTC(None));\n            req.add_property(plane, fb_id, property::Value::Framebuffer(None));\n        }\n\n        // Disable the CRTCs.\n        for crtc in cleanup {\n            let Some((mode_id, _, _)) = find_drm_property(&self.drm, crtc, \"MODE_ID\") else {\n                debug!(\"couldn't find CRTC MODE_ID property\");\n                continue;\n            };\n\n            let Some((active, _, _)) = find_drm_property(&self.drm, crtc, \"ACTIVE\") else {\n                debug!(\"couldn't find CRTC ACTIVE property\");\n                continue;\n            };\n\n            req.add_property(crtc, mode_id, property::Value::Unknown(0));\n            req.add_property(crtc, active, property::Value::Boolean(false));\n        }\n\n        self.drm\n            .atomic_commit(AtomicCommitFlags::ALLOW_MODESET, req)\n            .context(\"error doing atomic commit\")?;\n\n        Ok(())\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct TtyOutputState {\n    node: DrmNode,\n    crtc: crtc::Handle,\n}\n\nstruct Surface {\n    name: OutputName,\n    compositor: GbmDrmCompositor,\n    connector: connector::Handle,\n    dmabuf_feedback: Option<SurfaceDmabufFeedback>,\n    gamma_props: Option<GammaProps>,\n    /// Gamma change to apply upon session resume.\n    pending_gamma_change: Option<Option<Vec<u16>>>,\n    /// Tracy frame that goes from vblank to vblank.\n    vblank_frame: Option<tracy_client::Frame>,\n    /// Frame name for the VBlank frame.\n    vblank_frame_name: tracy_client::FrameName,\n    /// Plot name for the time since presentation plot.\n    time_since_presentation_plot_name: tracy_client::PlotName,\n    /// Plot name for the presentation misprediction plot.\n    presentation_misprediction_plot_name: tracy_client::PlotName,\n    sequence_delta_plot_name: tracy_client::PlotName,\n}\n\npub struct SurfaceDmabufFeedback {\n    pub render: DmabufFeedback,\n    pub scanout: DmabufFeedback,\n}\n\nstruct GammaProps {\n    crtc: crtc::Handle,\n    gamma_lut: property::Handle,\n    gamma_lut_size: property::Handle,\n    previous_blob: Option<NonZeroU64>,\n}\n\nstruct ConnectorProperties<'a> {\n    device: &'a DrmDevice,\n    connector: connector::Handle,\n    properties: Vec<(property::Info, property::RawValue)>,\n}\n\nimpl Tty {\n    pub fn new(\n        config: Rc<RefCell<Config>>,\n        event_loop: LoopHandle<'static, State>,\n    ) -> anyhow::Result<Self> {\n        let _span = tracy_client::span!(\"Tty::new\");\n\n        let (session, notifier) = LibSeatSession::new().context(\n            \"Error creating a session. This might mean that you're trying to run niri on a TTY \\\n             that is already busy, for example if you're running this inside tmux that had been \\\n             originally started on a different TTY\",\n        )?;\n        let seat_name = session.seat();\n\n        let udev_backend =\n            UdevBackend::new(session.seat()).context(\"error creating a udev backend\")?;\n        let udev_dispatcher = Dispatcher::new(udev_backend, move |event, _, state: &mut State| {\n            state.backend.tty().on_udev_event(&mut state.niri, event);\n        });\n        event_loop\n            .register_dispatcher(udev_dispatcher.clone())\n            .unwrap();\n\n        let mut libinput = Libinput::new_with_udev(LibinputSessionInterface::from(session.clone()));\n        unsafe { init_libinput_plugin_system(&libinput) };\n        {\n            let _span = tracy_client::span!(\"Libinput::udev_assign_seat\");\n            libinput.udev_assign_seat(&seat_name)\n        }\n        .map_err(|()| anyhow!(\"error assigning the seat to libinput\"))?;\n\n        let input_backend = LibinputInputBackend::new(libinput.clone());\n        event_loop\n            .insert_source(input_backend, |mut event, _, state| {\n                state.process_libinput_event(&mut event);\n                state.process_input_event(event);\n            })\n            .unwrap();\n\n        event_loop\n            .insert_source(notifier, move |event, _, state| {\n                state.backend.tty().on_session_event(&mut state.niri, event);\n            })\n            .unwrap();\n\n        let api = GbmGlesBackend::with_context_priority(ContextPriority::High);\n        let gpu_manager = GpuManager::new(api).context(\"error creating the GPU manager\")?;\n\n        let (primary_node, primary_render_node) = primary_node_from_config(&config.borrow())\n            .ok_or(())\n            .or_else(|()| {\n                let primary_gpu_path = udev::primary_gpu(&seat_name)\n                    .context(\"error getting the primary GPU\")?\n                    .context(\"couldn't find a GPU\")?;\n                let primary_node = DrmNode::from_path(primary_gpu_path)\n                    .context(\"error opening the primary GPU DRM node\")?;\n                let primary_render_node = primary_node\n                    .node_with_type(NodeType::Render)\n                    .and_then(Result::ok)\n                    .unwrap_or_else(|| {\n                        warn!(\n                            \"error getting the render node for the primary GPU; proceeding anyway\"\n                        );\n                        primary_node\n                    });\n\n                Ok::<_, anyhow::Error>((primary_node, primary_render_node))\n            })?;\n\n        let mut node_path = String::new();\n        if let Some(path) = primary_render_node.dev_path() {\n            write!(node_path, \"{path:?}\").unwrap();\n        } else {\n            write!(node_path, \"{primary_render_node}\").unwrap();\n        }\n        info!(\"using as the render node: {node_path}\");\n\n        let mut ignored_nodes = ignored_nodes_from_config(&config.borrow());\n        if ignored_nodes.remove(&primary_node) || ignored_nodes.remove(&primary_render_node) {\n            warn!(\"ignoring the primary node or render node is not allowed\");\n        }\n\n        Ok(Self {\n            config,\n            session,\n            udev_dispatcher,\n            libinput,\n            gpu_manager,\n            primary_node,\n            primary_render_node,\n            ignored_nodes,\n            devices: HashMap::new(),\n            dmabuf_global: None,\n            update_output_config_on_resume: false,\n            update_ignored_nodes_on_resume: false,\n            debug_tint: false,\n            ipc_outputs: Arc::new(Mutex::new(HashMap::new())),\n        })\n    }\n\n    pub fn init(&mut self, niri: &mut Niri) {\n        let udev = self.udev_dispatcher.clone();\n        let udev = udev.as_source_ref();\n\n        // Initialize the primary node first as later nodes might depend on the primary render node\n        // being available.\n        if let Some((primary_device_id, primary_device_path)) = udev\n            .device_list()\n            .find(|&(device_id, _)| device_id == self.primary_node.dev_id())\n        {\n            if let Err(err) = self.device_added(primary_device_id, primary_device_path, niri) {\n                warn!(\n                    \"error adding primary node device, display-only devices may not work: {err:?}\"\n                );\n            }\n        } else {\n            warn!(\"primary node is missing, display-only devices may not work\");\n        };\n\n        for (device_id, path) in udev.device_list() {\n            if device_id == self.primary_node.dev_id() {\n                continue;\n            }\n\n            if let Err(err) = self.device_added(device_id, path, niri) {\n                warn!(\"error adding device: {err:?}\");\n            }\n        }\n    }\n\n    fn on_udev_event(&mut self, niri: &mut Niri, event: UdevEvent) {\n        let _span = tracy_client::span!(\"Tty::on_udev_event\");\n\n        match event {\n            UdevEvent::Added { device_id, path } => {\n                if !self.session.is_active() {\n                    debug!(\"skipping UdevEvent::Added as session is inactive\");\n                    return;\n                }\n\n                if let Err(err) = self.device_added(device_id, &path, niri) {\n                    warn!(\"error adding device: {err:?}\");\n                }\n            }\n            UdevEvent::Changed { device_id } => {\n                if !self.session.is_active() {\n                    debug!(\"skipping UdevEvent::Changed as session is inactive\");\n                    return;\n                }\n\n                self.device_changed(device_id, niri, false)\n            }\n            UdevEvent::Removed { device_id } => {\n                if !self.session.is_active() {\n                    debug!(\"skipping UdevEvent::Removed as session is inactive\");\n                    return;\n                }\n\n                self.device_removed(device_id, niri)\n            }\n        }\n    }\n\n    fn on_session_event(&mut self, niri: &mut Niri, event: SessionEvent) {\n        let _span = tracy_client::span!(\"Tty::on_session_event\");\n\n        match event {\n            SessionEvent::PauseSession => {\n                debug!(\"pausing session\");\n\n                self.libinput.suspend();\n\n                for device in self.devices.values_mut() {\n                    device.drm.pause();\n\n                    if let Some(lease_state) = &mut device.drm_lease_state {\n                        lease_state.suspend();\n                    }\n                }\n            }\n            SessionEvent::ActivateSession => {\n                debug!(\"resuming session\");\n\n                if self.libinput.resume().is_err() {\n                    warn!(\"error resuming libinput\");\n                }\n\n                if self.update_ignored_nodes_on_resume {\n                    self.update_ignored_nodes_on_resume = false;\n                    let mut ignored_nodes = ignored_nodes_from_config(&self.config.borrow());\n                    if ignored_nodes.remove(&self.primary_node)\n                        || ignored_nodes.remove(&self.primary_render_node)\n                    {\n                        warn!(\"ignoring the primary node or render node is not allowed\");\n                    }\n                    self.ignored_nodes = ignored_nodes;\n                }\n\n                let mut device_list = self\n                    .udev_dispatcher\n                    .as_source_ref()\n                    .device_list()\n                    .map(|(device_id, path)| (device_id, path.to_owned()))\n                    .collect::<HashMap<_, _>>();\n\n                let removed_devices = self\n                    .devices\n                    .keys()\n                    .filter(|node| {\n                        !device_list.contains_key(&node.dev_id())\n                            || self.ignored_nodes.contains(node)\n                    })\n                    .copied()\n                    .collect::<Vec<_>>();\n\n                let remained_devices = self\n                    .devices\n                    .keys()\n                    .filter(|node| {\n                        device_list.contains_key(&node.dev_id())\n                            && !self.ignored_nodes.contains(node)\n                    })\n                    .copied()\n                    .collect::<Vec<_>>();\n\n                // Remove removed devices.\n                for node in removed_devices {\n                    device_list.remove(&node.dev_id());\n                    self.device_removed(node.dev_id(), niri);\n                }\n\n                // Update remained devices.\n                for node in remained_devices {\n                    device_list.remove(&node.dev_id());\n\n                    // It hasn't been removed, update its state as usual.\n                    let device = self.devices.get_mut(&node).unwrap();\n\n                    // Someone on an old device hit what seems to be a driver bug without this:\n                    // https://github.com/niri-wm/niri/issues/3048\n                    let force_disable = self\n                        .config\n                        .borrow()\n                        .debug\n                        .force_disable_connectors_on_resume;\n\n                    if let Err(err) = device.drm.activate(force_disable) {\n                        warn!(\"error activating DRM device: {err:?}\");\n                    }\n                    if let Some(lease_state) = &mut device.drm_lease_state {\n                        lease_state.resume::<State>();\n                    }\n\n                    // Refresh the connectors.\n                    self.device_changed(node.dev_id(), niri, true);\n\n                    // Apply pending gamma changes and restore our existing gamma.\n                    let device = self.devices.get_mut(&node).unwrap();\n                    for (crtc, surface) in device.surfaces.iter_mut() {\n                        if let Ok(props) =\n                            ConnectorProperties::try_new(&device.drm, surface.connector)\n                        {\n                            match reset_hdr(&props) {\n                                Ok(()) => (),\n                                Err(err) => debug!(\"couldn't reset HDR properties: {err:?}\"),\n                            }\n                        } else {\n                            warn!(\"failed to get connector properties\");\n                        };\n\n                        if let Some(ramp) = surface.pending_gamma_change.take() {\n                            let ramp = ramp.as_deref();\n                            let res = if let Some(gamma_props) = &mut surface.gamma_props {\n                                gamma_props.set_gamma(&device.drm, ramp)\n                            } else {\n                                set_gamma_for_crtc(&device.drm, *crtc, ramp)\n                            };\n                            if let Err(err) = res {\n                                warn!(\"error applying pending gamma change: {err:?}\");\n                            }\n                        } else if let Some(gamma_props) = &surface.gamma_props {\n                            if let Err(err) = gamma_props.restore_gamma(&device.drm) {\n                                warn!(\"error restoring gamma: {err:?}\");\n                            }\n                        }\n                    }\n                }\n\n                // Add new devices.\n                for (device_id, path) in device_list.into_iter() {\n                    if let Err(err) = self.device_added(device_id, &path, niri) {\n                        warn!(\"error adding device: {err:?}\");\n                    }\n                }\n\n                if self.update_output_config_on_resume {\n                    self.on_output_config_changed(niri);\n                }\n\n                self.refresh_ipc_outputs(niri);\n\n                niri.notify_activity();\n                niri.monitors_active = true;\n                self.set_monitors_active(true);\n                niri.queue_redraw_all();\n            }\n        }\n    }\n\n    fn device_added(\n        &mut self,\n        device_id: dev_t,\n        path: &Path,\n        niri: &mut Niri,\n    ) -> anyhow::Result<()> {\n        debug!(\"adding device: {device_id} {path:?}\");\n\n        let node = DrmNode::from_dev_id(device_id)?;\n\n        if node == self.primary_node {\n            debug!(\"this is the primary node\");\n        }\n\n        // Only consider primary node on udev event\n        // https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/768fbaad54027f8dd027e7e015e8eeb93cb38c52\n        if node.ty() != NodeType::Primary {\n            debug!(\"not a primary node, skipping\");\n            return Ok(());\n        }\n\n        if self.ignored_nodes.contains(&node) {\n            debug!(\"node is ignored, skipping\");\n            return Ok(());\n        }\n\n        let _span = tracy_client::span!(\"Tty::device_added\");\n\n        let open_flags = OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK;\n        let fd = {\n            let _span = tracy_client::span!(\"LibSeatSession::open\");\n            self.session.open(path, open_flags)\n        }?;\n        let device_fd = DrmDeviceFd::new(DeviceFd::from(fd));\n\n        let (drm, drm_notifier) = {\n            let _span = tracy_client::span!(\"DrmDevice::new\");\n            DrmDevice::new(device_fd.clone(), false)\n        }?;\n        let gbm = {\n            let _span = tracy_client::span!(\"GbmDevice::new\");\n            GbmDevice::new(device_fd)\n        }?;\n\n        let mut try_initialize_gpu = || {\n            let display = unsafe { EGLDisplay::new(gbm.clone())? };\n            let egl_device = EGLDevice::device_for_display(&display)?;\n\n            // Software EGL devices (e.g., llvmpipe/softpipe) are rejected for now. They have some\n            // problems (segfault on importing dmabufs from other renderers) and need to be\n            // excluded from some places like DRM leasing.\n            ensure!(\n                !egl_device.is_software(),\n                \"software EGL renderers are skipped\"\n            );\n\n            let render_node = egl_device\n                .try_get_render_node()\n                .ok()\n                .flatten()\n                .unwrap_or(node);\n            self.gpu_manager\n                .as_mut()\n                .add_node(render_node, gbm.clone())\n                .context(\"error adding render node to GPU manager\")?;\n\n            Ok(render_node)\n        };\n\n        let render_node = match try_initialize_gpu() {\n            Ok(render_node) => {\n                debug!(\"got render node: {render_node}\");\n                Some(render_node)\n            }\n            Err(err) => {\n                debug!(\"failed to initialize renderer, falling back to primary gpu: {err:?}\");\n                None\n            }\n        };\n\n        if render_node == Some(self.primary_render_node) && self.dmabuf_global.is_none() {\n            let render_node = self.primary_render_node;\n            debug!(\"initializing the primary renderer\");\n\n            let mut renderer = self\n                .gpu_manager\n                .single_renderer(&render_node)\n                .context(\"error creating renderer\")?;\n\n            if let Err(err) = renderer.bind_wl_display(&niri.display_handle) {\n                warn!(\"error binding wl-display in EGL: {err:?}\");\n            }\n\n            let gles_renderer = renderer.as_gles_renderer();\n            resources::init(gles_renderer);\n            shaders::init(gles_renderer);\n\n            let config = self.config.borrow();\n            if let Some(src) = config.animations.window_resize.custom_shader.as_deref() {\n                shaders::set_custom_resize_program(gles_renderer, Some(src));\n            }\n            if let Some(src) = config.animations.window_close.custom_shader.as_deref() {\n                shaders::set_custom_close_program(gles_renderer, Some(src));\n            }\n            if let Some(src) = config.animations.window_open.custom_shader.as_deref() {\n                shaders::set_custom_open_program(gles_renderer, Some(src));\n            }\n            drop(config);\n\n            niri.update_shaders();\n\n            // Create the dmabuf global.\n            let primary_formats = renderer.dmabuf_formats();\n            let default_feedback =\n                DmabufFeedbackBuilder::new(render_node.dev_id(), primary_formats.clone())\n                    .build()\n                    .context(\"error building default dmabuf feedback\")?;\n            let dmabuf_global = niri\n                .dmabuf_state\n                .create_global_with_default_feedback::<State>(\n                    &niri.display_handle,\n                    &default_feedback,\n                );\n            assert!(self.dmabuf_global.replace(dmabuf_global).is_none());\n\n            // Update the dmabuf feedbacks for all surfaces.\n            for (node, device) in self.devices.iter_mut() {\n                for surface in device.surfaces.values_mut() {\n                    match surface_dmabuf_feedback(\n                        &surface.compositor,\n                        primary_formats.clone(),\n                        self.primary_render_node,\n                        device.render_node,\n                        *node,\n                    ) {\n                        Ok(feedback) => {\n                            surface.dmabuf_feedback = Some(feedback);\n                        }\n                        Err(err) => {\n                            warn!(\"error building dmabuf feedback: {err:?}\");\n                        }\n                    }\n                }\n            }\n        }\n\n        let allocator_gbm = if render_node.is_some() {\n            gbm.clone()\n        } else if let Some(primary_device) = self.devices.get(&self.primary_node) {\n            primary_device.gbm.clone()\n        } else {\n            bail!(\"no allocator available for device\");\n        };\n        let gbm_flags = GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT;\n        let allocator = GbmAllocator::new(allocator_gbm, gbm_flags);\n\n        let token = niri\n            .event_loop\n            .insert_source(drm_notifier, move |event, meta, state| {\n                let tty = state.backend.tty();\n                match event {\n                    DrmEvent::VBlank(crtc) => {\n                        let meta = meta.expect(\"VBlank events must have metadata\");\n                        tty.on_vblank(&mut state.niri, node, crtc, meta);\n                    }\n                    DrmEvent::Error(error) => warn!(\"DRM error: {error}\"),\n                };\n            })\n            .unwrap();\n\n        let drm_lease_state = DrmLeaseState::new::<State>(&niri.display_handle, &node)\n            .map_err(|err| warn!(\"error initializing DRM leasing for {node}: {err:?}\"))\n            .ok();\n\n        let device = OutputDevice {\n            token,\n            render_node,\n            drm,\n            gbm,\n            allocator,\n            drm_scanner: DrmScanner::new(),\n            surfaces: HashMap::new(),\n            known_crtcs: HashMap::new(),\n            drm_lease_state,\n            active_leases: Vec::new(),\n            non_desktop_connectors: HashSet::new(),\n        };\n        assert!(self.devices.insert(node, device).is_none());\n\n        self.device_changed(device_id, niri, true);\n\n        Ok(())\n    }\n\n    fn device_changed(&mut self, device_id: dev_t, niri: &mut Niri, cleanup: bool) {\n        debug!(\"device changed: {device_id}\");\n\n        let Ok(node) = DrmNode::from_dev_id(device_id) else {\n            warn!(\"error creating DrmNode\");\n            return;\n        };\n\n        if node.ty() != NodeType::Primary {\n            debug!(\"not a primary node, skipping\");\n            return;\n        }\n\n        if self.ignored_nodes.contains(&node) {\n            debug!(\"node is ignored, skipping\");\n            return;\n        }\n\n        let Some(device) = self.devices.get_mut(&node) else {\n            if let Some(path) = node.dev_path() {\n                warn!(\"unknown device; trying to add\");\n\n                if let Err(err) = self.device_added(device_id, &path, niri) {\n                    warn!(\"error adding device: {err:?}\");\n                }\n            } else {\n                warn!(\"unknown device\");\n            }\n\n            return;\n        };\n\n        // DrmScanner will preserve any existing connector-CRTC mapping.\n        let scan_result = match device.drm_scanner.scan_connectors(&device.drm) {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error scanning connectors: {err:?}\");\n                return;\n            }\n        };\n\n        let mut added = Vec::new();\n        let mut removed = Vec::new();\n        for event in scan_result {\n            match event {\n                DrmScanEvent::Connected {\n                    connector,\n                    crtc: Some(crtc),\n                } => {\n                    let connector_name = format_connector_name(&connector);\n                    let name = make_output_name(&device.drm, connector.handle(), connector_name);\n                    debug!(\n                        \"new connector: {} \\\"{}\\\"\",\n                        &name.connector,\n                        name.format_make_model_serial(),\n                    );\n\n                    // Assign an id to this crtc.\n                    let id = OutputId::next();\n                    added.push((crtc, CrtcInfo { id, name }));\n                }\n                DrmScanEvent::Disconnected {\n                    crtc: Some(crtc), ..\n                } => {\n                    removed.push(crtc);\n                }\n                // Emitted when the list of connector modes changes at runtime.\n                //\n                // Some devices, notably USB-C docks with DP-MST/alt-mode, report Connected before\n                // the EDID has been read, with an empty mode list. Then, at a later point, the\n                // modes will be populated, at which point we'll get this Changed event.\n                DrmScanEvent::Changed {\n                    connector,\n                    crtc: Some(crtc),\n                } => {\n                    let connector_name = format_connector_name(&connector);\n                    let name = make_output_name(&device.drm, connector.handle(), connector_name);\n                    debug!(\n                        \"connector changed: {} \\\"{}\\\"\",\n                        &name.connector,\n                        name.format_make_model_serial(),\n                    );\n\n                    if !device.known_crtcs.contains_key(&crtc) {\n                        // I guess this can happen if the connector initially wasn't mapped to a\n                        // CRTC but then got mapped before being changed.\n                        warn!(\"changed connector missing from known crtcs\");\n                    }\n\n                    // We don't actually need to do anything here; on_output_config_changed() will\n                    // take care of picking a new mode if needed.\n                }\n                _ => (),\n            }\n        }\n\n        for crtc in &removed {\n            self.connector_disconnected(niri, node, *crtc);\n        }\n\n        let Some(device) = self.devices.get_mut(&node) else {\n            error!(\"device disappeared\");\n            return;\n        };\n\n        for crtc in removed {\n            if device.known_crtcs.remove(&crtc).is_none() {\n                error!(\"output ID missing for disconnected crtc: {crtc:?}\");\n            }\n        }\n\n        for (crtc, mut info) in added {\n            // Make/model/serial can match exactly between different physical monitors. This doesn't\n            // happen often, but our Layout does not support such duplicates and will panic.\n            //\n            // As a workaround, search for duplicates, and unname the new connectors if one is\n            // found. Connector names are always unique.\n            let name = &mut info.name;\n            let formatted = name.format_make_model_serial_or_connector();\n            for info in self.devices.values().flat_map(|d| d.known_crtcs.values()) {\n                if info.name.matches(&formatted) {\n                    let connector = mem::take(&mut name.connector);\n                    warn!(\n                        \"new connector {connector} duplicates make/model/serial \\\n                         of existing connector {}, unnaming\",\n                        info.name.connector,\n                    );\n                    *name = OutputName {\n                        connector,\n                        make: None,\n                        model: None,\n                        serial: None,\n                    };\n                    break;\n                }\n            }\n\n            // Insert it right away so next added connector will check against this one too.\n            let device = self.devices.get_mut(&node).unwrap();\n            device.known_crtcs.insert(crtc, info);\n        }\n\n        // If the device was just added or resumed, we need to cleanup any disconnected connectors\n        // and planes.\n        if cleanup {\n            let device = self.devices.get(&node).unwrap();\n\n            // Follow the logic in on_output_config_changed().\n            let disable_laptop_panels = self.should_disable_laptop_panels(niri.is_lid_closed);\n            let should_disable = |conn: &str| disable_laptop_panels && is_laptop_panel(conn);\n\n            let config = self.config.borrow();\n            let disable_monitor_names = config.debug.disable_monitor_names;\n\n            let should_be_off = |crtc, conn: &connector::Info| {\n                let output_name = device.known_crtc_name(&crtc, conn, disable_monitor_names);\n\n                let config = config\n                    .outputs\n                    .find(&output_name)\n                    .cloned()\n                    .unwrap_or_default();\n\n                config.off || should_disable(&output_name.connector)\n            };\n\n            if let Err(err) = device.cleanup_mismatching_resources(&should_be_off) {\n                warn!(\"error cleaning up connectors: {err:?}\");\n            }\n\n            let device = self.devices.get_mut(&node).unwrap();\n            for surface in device.surfaces.values_mut() {\n                // We aren't force-clearing the CRTCs, so we need to make the surfaces read the\n                // updated state after a session resume. This also causes a full damage for the\n                // next redraw.\n                if let Err(err) = surface.compositor.reset_state() {\n                    warn!(\"error resetting DrmCompositor state: {err:?}\");\n                }\n                surface.compositor.reset_buffers();\n            }\n        }\n\n        // This will connect any new connectors if needed, and apply other changes, such as\n        // connecting back the internal laptop monitor once it becomes the only monitor left.\n        //\n        // It will also call refresh_ipc_outputs(), which will catch the disconnected connectors\n        // above.\n        self.on_output_config_changed(niri);\n    }\n\n    fn device_removed(&mut self, device_id: dev_t, niri: &mut Niri) {\n        debug!(\"removing device: {device_id}\");\n\n        let Ok(node) = DrmNode::from_dev_id(device_id) else {\n            warn!(\"error creating DrmNode\");\n            return;\n        };\n\n        if node.ty() != NodeType::Primary {\n            debug!(\"not a primary node, skipping\");\n            return;\n        }\n\n        let Some(device) = self.devices.get_mut(&node) else {\n            warn!(\"unknown device\");\n            return;\n        };\n\n        let crtcs: Vec<_> = device\n            .drm_scanner\n            .crtcs()\n            .map(|(_info, crtc)| crtc)\n            .collect();\n\n        for crtc in crtcs {\n            self.connector_disconnected(niri, node, crtc);\n        }\n\n        let mut device = self.devices.remove(&node).unwrap();\n        let device_fd = device.drm.device_fd().device_fd();\n\n        if let Some(lease_state) = &mut device.drm_lease_state {\n            lease_state.disable_global::<State>();\n        }\n\n        if let Some(render_node) = device.render_node {\n            // Sometimes (Asahi DisplayLink), multiple primary nodes will correspond to the same\n            // render node. In this case, we want to keep the render node active until the last\n            // primary node that uses it is gone.\n            let was_last = !self\n                .devices\n                .values()\n                .any(|device| device.render_node == Some(render_node));\n\n            if was_last && render_node == self.primary_render_node {\n                debug!(\"destroying the primary renderer\");\n\n                match self.gpu_manager.single_renderer(&self.primary_render_node) {\n                    Ok(mut renderer) => renderer.unbind_wl_display(),\n                    Err(err) => {\n                        warn!(\"error creating renderer during device removal: {err}\");\n                    }\n                }\n\n                // Disable and destroy the dmabuf global.\n                if let Some(global) = self.dmabuf_global.take() {\n                    niri.dmabuf_state\n                        .disable_global::<State>(&niri.display_handle, &global);\n                    niri.event_loop\n                        .insert_source(\n                            Timer::from_duration(Duration::from_secs(10)),\n                            move |_, _, state| {\n                                state\n                                    .niri\n                                    .dmabuf_state\n                                    .destroy_global::<State>(&state.niri.display_handle, global);\n                                TimeoutAction::Drop\n                            },\n                        )\n                        .unwrap();\n\n                    // Clear the dmabuf feedbacks for all surfaces.\n                    for device in self.devices.values_mut() {\n                        for surface in device.surfaces.values_mut() {\n                            surface.dmabuf_feedback = None;\n                        }\n                    }\n                } else {\n                    error!(\"dmabuf global was already missing\");\n                }\n            }\n\n            if was_last {\n                self.gpu_manager.as_mut().remove_node(&render_node);\n                // Trigger re-enumeration in order to remove the device from gpu_manager.\n                let _ = self.gpu_manager.devices();\n            }\n        }\n\n        niri.event_loop.remove(device.token);\n\n        self.refresh_ipc_outputs(niri);\n\n        drop(device);\n\n        match TryInto::<OwnedFd>::try_into(device_fd) {\n            Ok(fd) => {\n                if let Err(err) = self.session.close(fd) {\n                    warn!(\"error closing DRM device fd: {err:?}\");\n                }\n            }\n            Err(_) => {\n                error!(\"unable to close DRM device cleanly: fd has unexpected references\");\n            }\n        }\n    }\n\n    fn connector_connected(\n        &mut self,\n        niri: &mut Niri,\n        node: DrmNode,\n        connector: connector::Info,\n        crtc: crtc::Handle,\n    ) -> anyhow::Result<()> {\n        let connector_name = format_connector_name(&connector);\n        debug!(\"connecting connector: {connector_name}\");\n\n        let device = self.devices.get_mut(&node).context(\"missing device\")?;\n\n        let disable_monitor_names = self.config.borrow().debug.disable_monitor_names;\n        let output_name = device.known_crtc_name(&crtc, &connector, disable_monitor_names);\n\n        let non_desktop = find_drm_property(&device.drm, connector.handle(), \"non-desktop\")\n            .and_then(|(_, info, value)| info.value_type().convert_value(value).as_boolean())\n            .unwrap_or(false);\n\n        if non_desktop {\n            debug!(\"output is non desktop\");\n            let description = output_name.format_description();\n            if let Some(lease_state) = &mut device.drm_lease_state {\n                lease_state.add_connector::<State>(connector.handle(), connector_name, description);\n            }\n            device\n                .non_desktop_connectors\n                .insert((connector.handle(), crtc));\n            return Ok(());\n        }\n\n        let config = self\n            .config\n            .borrow()\n            .outputs\n            .find(&output_name)\n            .cloned()\n            .unwrap_or_default();\n\n        for m in connector.modes() {\n            trace!(\"{m:?}\");\n        }\n\n        let mut mode = None;\n        if let Some(modeline) = &config.modeline {\n            match calculate_drm_mode_from_modeline(modeline) {\n                Ok(x) => mode = Some(x),\n                Err(err) => {\n                    warn!(\"invalid custom modeline; falling back to advertised modes: {err:?}\");\n                }\n            }\n        }\n\n        let (mode, fallback) = match mode {\n            Some(x) => (x, false),\n            None => pick_mode(&connector, config.mode).ok_or_else(|| anyhow!(\"no mode\"))?,\n        };\n\n        if fallback {\n            let target = config.mode.unwrap();\n            warn!(\n                \"configured mode {}x{}{} could not be found, falling back to preferred\",\n                target.mode.width,\n                target.mode.height,\n                if let Some(refresh) = target.mode.refresh {\n                    format!(\"@{refresh}\")\n                } else {\n                    String::new()\n                },\n            );\n        }\n\n        debug!(\"picking mode: {mode:?}\");\n\n        let mut orientation = None;\n        if let Ok(props) = ConnectorProperties::try_new(&device.drm, connector.handle()) {\n            match reset_hdr(&props) {\n                Ok(()) => (),\n                Err(err) => debug!(\"couldn't reset HDR properties: {err:?}\"),\n            }\n\n            match get_panel_orientation(&props) {\n                Ok(x) => orientation = Some(x),\n                Err(err) => {\n                    trace!(\"couldn't get panel orientation: {err:?}\");\n                }\n            }\n        } else {\n            warn!(\"failed to get connector properties\");\n        };\n\n        let mut gamma_props = GammaProps::new(&device.drm, crtc)\n            .map_err(|err| debug!(\"couldn't get gamma properties: {err:?}\"))\n            .ok();\n\n        // Reset gamma in case it was set before.\n        let res = if let Some(gamma_props) = &mut gamma_props {\n            gamma_props.set_gamma(&device.drm, None)\n        } else {\n            set_gamma_for_crtc(&device.drm, crtc, None)\n        };\n        if let Err(err) = res {\n            debug!(\"couldn't reset gamma: {err:?}\");\n        }\n\n        let surface = device\n            .drm\n            .create_surface(crtc, mode, &[connector.handle()])?;\n\n        // Try to enable VRR if requested.\n        match surface.vrr_supported(connector.handle()) {\n            Ok(VrrSupport::Supported | VrrSupport::RequiresModeset) => {\n                // Even if on-demand, we still disable it until later checks.\n                let vrr = config.is_vrr_always_on();\n                let word = if vrr { \"enabling\" } else { \"disabling\" };\n\n                if let Err(err) = surface.use_vrr(vrr) {\n                    warn!(\"error {} VRR: {err:?}\", word);\n                }\n            }\n            Ok(VrrSupport::NotSupported) => {\n                if !config.is_vrr_always_off() {\n                    warn!(\"cannot enable VRR because connector does not support it\");\n                }\n\n                // Try to disable it anyway to work around a bug where resetting DRM state causes\n                // vrr_capable to be reset to 0, potentially leaving VRR_ENABLED at 1.\n                let _ = surface.use_vrr(false);\n            }\n            Err(err) => {\n                warn!(\"error querying for VRR support: {err:?}\");\n            }\n        }\n\n        // Update the output mode.\n        let (physical_width, physical_height) = connector.size().unwrap_or((0, 0));\n\n        let output = Output::new(\n            connector_name.clone(),\n            PhysicalProperties {\n                size: (physical_width as i32, physical_height as i32).into(),\n                subpixel: connector.subpixel().into(),\n                model: output_name.model.as_deref().unwrap_or(\"Unknown\").to_owned(),\n                make: output_name.make.as_deref().unwrap_or(\"Unknown\").to_owned(),\n                serial_number: output_name\n                    .serial\n                    .as_deref()\n                    .unwrap_or(\"Unknown\")\n                    .to_owned(),\n            },\n        );\n\n        let wl_mode = Mode::from(mode);\n        output.change_current_state(Some(wl_mode), None, None, None);\n        output.set_preferred(wl_mode);\n\n        output\n            .user_data()\n            .insert_if_missing(|| TtyOutputState { node, crtc });\n        output.user_data().insert_if_missing(|| output_name.clone());\n        if let Some(x) = orientation {\n            output.user_data().insert_if_missing(|| PanelOrientation(x));\n        }\n\n        let render_node = device.render_node.unwrap_or(self.primary_render_node);\n        let renderer = self.gpu_manager.single_renderer(&render_node)?;\n        let egl_context = renderer.as_ref().egl_context();\n        let render_formats = egl_context.dmabuf_render_formats();\n\n        // Filter out the CCS modifiers as they have increased bandwidth, causing some monitor\n        // configurations to stop working.\n        //\n        // For display only devices, restrict to linear buffers for best compatibility.\n        //\n        // The invalid modifier attempt below should make this unnecessary in some cases, but it\n        // would still be a bad idea to remove this until Smithay has some kind of full-device\n        // modesetting test that is able to \"downgrade\" existing connector modifiers to get enough\n        // bandwidth for a newly connected one.\n        let render_formats = render_formats\n            .iter()\n            .copied()\n            .filter(|format| {\n                if device.render_node.is_none() {\n                    return format.modifier == Modifier::Linear;\n                }\n\n                let is_ccs = matches!(\n                    format.modifier,\n                    Modifier::I915_y_tiled_ccs\n                    // I915_FORMAT_MOD_Yf_TILED_CCS\n                    | Modifier::Unrecognized(0x100000000000005)\n                    | Modifier::I915_y_tiled_gen12_rc_ccs\n                    | Modifier::I915_y_tiled_gen12_mc_ccs\n                    // I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC\n                    | Modifier::Unrecognized(0x100000000000008)\n                    // I915_FORMAT_MOD_4_TILED_DG2_RC_CCS\n                    | Modifier::Unrecognized(0x10000000000000a)\n                    // I915_FORMAT_MOD_4_TILED_DG2_MC_CCS\n                    | Modifier::Unrecognized(0x10000000000000b)\n                    // I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC\n                    | Modifier::Unrecognized(0x10000000000000c)\n                );\n\n                !is_ccs\n            })\n            .collect::<FormatSet>();\n\n        // Create the compositor.\n        let res = DrmCompositor::new(\n            OutputModeSource::Auto(output.clone()),\n            surface,\n            None,\n            device.allocator.clone(),\n            GbmFramebufferExporter::new(device.gbm.clone(), device.render_node.into()),\n            SUPPORTED_COLOR_FORMATS,\n            // This is only used to pick a good internal format, so it can use the surface's render\n            // formats, even though we only ever render on the primary GPU.\n            render_formats.clone(),\n            device.drm.cursor_size(),\n            Some(device.gbm.clone()),\n        );\n\n        let mut compositor = match res {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error creating DRM compositor, will try with invalid modifier: {err:?}\");\n\n                let render_formats = render_formats\n                    .iter()\n                    .copied()\n                    .filter(|format| format.modifier == Modifier::Invalid)\n                    .collect::<FormatSet>();\n\n                // DrmCompositor::new() consumed the surface...\n                let surface = device\n                    .drm\n                    .create_surface(crtc, mode, &[connector.handle()])?;\n\n                DrmCompositor::new(\n                    OutputModeSource::Auto(output.clone()),\n                    surface,\n                    None,\n                    device.allocator.clone(),\n                    GbmFramebufferExporter::new(device.gbm.clone(), device.render_node.into()),\n                    SUPPORTED_COLOR_FORMATS,\n                    render_formats,\n                    device.drm.cursor_size(),\n                    Some(device.gbm.clone()),\n                )\n                .context(\"error creating DRM compositor\")?\n            }\n        };\n\n        if self.debug_tint {\n            compositor.set_debug_flags(DebugFlags::TINT);\n        }\n\n        let mut dmabuf_feedback = None;\n        if let Ok(primary_renderer) = self.gpu_manager.single_renderer(&self.primary_render_node) {\n            let primary_formats = primary_renderer.dmabuf_formats();\n\n            match surface_dmabuf_feedback(\n                &compositor,\n                primary_formats,\n                self.primary_render_node,\n                device.render_node,\n                node,\n            ) {\n                Ok(feedback) => {\n                    dmabuf_feedback = Some(feedback);\n                }\n                Err(err) => {\n                    warn!(\"error building dmabuf feedback: {err:?}\");\n                }\n            }\n        }\n\n        // Some buggy monitors replug upon powering off, so powering on here would prevent such\n        // monitors from powering off. Therefore, we avoid unconditionally powering on.\n        if !niri.monitors_active {\n            if let Err(err) = compositor.clear() {\n                warn!(\"error clearing drm surface: {err:?}\");\n            }\n        }\n\n        let vrr_enabled = compositor.vrr_enabled();\n\n        let vblank_frame_name =\n            tracy_client::FrameName::new_leak(format!(\"vblank on {connector_name}\"));\n        let time_since_presentation_plot_name = tracy_client::PlotName::new_leak(format!(\n            \"{connector_name} time since presentation, ms\"\n        ));\n        let presentation_misprediction_plot_name = tracy_client::PlotName::new_leak(format!(\n            \"{connector_name} presentation misprediction, ms\"\n        ));\n        let sequence_delta_plot_name =\n            tracy_client::PlotName::new_leak(format!(\"{connector_name} sequence delta\"));\n\n        let surface = Surface {\n            name: output_name,\n            connector: connector.handle(),\n            compositor,\n            dmabuf_feedback,\n            gamma_props,\n            pending_gamma_change: None,\n            vblank_frame: None,\n            vblank_frame_name,\n            time_since_presentation_plot_name,\n            presentation_misprediction_plot_name,\n            sequence_delta_plot_name,\n        };\n\n        let res = device.surfaces.insert(crtc, surface);\n        assert!(res.is_none(), \"crtc must not have already existed\");\n\n        niri.add_output(output.clone(), Some(refresh_interval(mode)), vrr_enabled);\n\n        if niri.monitors_active {\n            // Redraw the new monitor.\n            niri.event_loop.insert_idle(move |state| {\n                // Guard against output disconnecting before the idle has a chance to run.\n                if state.niri.output_state.contains_key(&output) {\n                    state.niri.queue_redraw(&output);\n                }\n            });\n        }\n\n        Ok(())\n    }\n\n    fn connector_disconnected(&mut self, niri: &mut Niri, node: DrmNode, crtc: crtc::Handle) {\n        let Some(device) = self.devices.get_mut(&node) else {\n            debug!(\"disconnecting connector for crtc: {crtc:?}\");\n            error!(\"missing device\");\n            return;\n        };\n\n        let Some(surface) = device.surfaces.remove(&crtc) else {\n            debug!(\"disconnecting connector for crtc: {crtc:?}\");\n\n            if let Some((conn, _)) = device\n                .non_desktop_connectors\n                .iter()\n                .find(|(_, crtc_)| *crtc_ == crtc)\n            {\n                debug!(\"withdrawing non-desktop connector from DRM leasing\");\n\n                let conn = *conn;\n                device.non_desktop_connectors.remove(&(conn, crtc));\n\n                if let Some(lease_state) = &mut device.drm_lease_state {\n                    lease_state.withdraw_connector(conn);\n                }\n            } else {\n                debug!(\"crtc wasn't enabled\");\n            }\n\n            return;\n        };\n\n        debug!(\"disconnecting connector: {:?}\", surface.name.connector);\n\n        let output = niri\n            .global_space\n            .outputs()\n            .find(|output| {\n                let tty_state: &TtyOutputState = output.user_data().get().unwrap();\n                tty_state.node == node && tty_state.crtc == crtc\n            })\n            .cloned();\n        if let Some(output) = output {\n            niri.remove_output(&output);\n        } else {\n            error!(\"missing output for crtc {crtc:?}\");\n        };\n    }\n\n    fn on_vblank(\n        &mut self,\n        niri: &mut Niri,\n        node: DrmNode,\n        crtc: crtc::Handle,\n        meta: DrmEventMetadata,\n    ) {\n        let span = tracy_client::span!(\"Tty::on_vblank\");\n\n        let now = get_monotonic_time();\n\n        let Some(device) = self.devices.get_mut(&node) else {\n            // I've seen it happen.\n            error!(\"missing device in vblank callback for crtc {crtc:?}\");\n            return;\n        };\n\n        let Some(surface) = device.surfaces.get_mut(&crtc) else {\n            error!(\"missing surface in vblank callback for crtc {crtc:?}\");\n            return;\n        };\n\n        // Finish the Tracy frame, if any.\n        drop(surface.vblank_frame.take());\n\n        let name = &surface.name.connector;\n        trace!(\"vblank on {name} {meta:?}\");\n        span.emit_text(name);\n\n        let presentation_time = match meta.time {\n            DrmEventTime::Monotonic(time) => time,\n            DrmEventTime::Realtime(_) => {\n                // Not supported.\n\n                // This value will be ignored in the frame clock code.\n                Duration::ZERO\n            }\n        };\n        let presentation_time = if niri.config.borrow().debug.emulate_zero_presentation_time {\n            Duration::ZERO\n        } else {\n            presentation_time\n        };\n\n        let message = if presentation_time.is_zero() {\n            format!(\"vblank on {name}, presentation time unknown\")\n        } else if presentation_time > now {\n            let diff = presentation_time - now;\n            tracy_client::Client::running().unwrap().plot(\n                surface.time_since_presentation_plot_name,\n                -diff.as_secs_f64() * 1000.,\n            );\n            format!(\"vblank on {name}, presentation is {diff:?} later\")\n        } else {\n            let diff = now - presentation_time;\n            tracy_client::Client::running().unwrap().plot(\n                surface.time_since_presentation_plot_name,\n                diff.as_secs_f64() * 1000.,\n            );\n            format!(\"vblank on {name}, presentation was {diff:?} ago\")\n        };\n        tracy_client::Client::running()\n            .unwrap()\n            .message(&message, 0);\n\n        let Some(output) = niri\n            .global_space\n            .outputs()\n            .find(|output| {\n                let tty_state: &TtyOutputState = output.user_data().get().unwrap();\n                tty_state.node == node && tty_state.crtc == crtc\n            })\n            .cloned()\n        else {\n            error!(\"missing output in global space for {name}\");\n            return;\n        };\n\n        let Some(output_state) = niri.output_state.get_mut(&output) else {\n            error!(\"missing output state for {name}\");\n            return;\n        };\n\n        let refresh_interval = output_state.frame_clock.refresh_interval();\n\n        let time = if presentation_time.is_zero() {\n            now\n        } else {\n            presentation_time\n        };\n\n        if output_state\n            .vblank_throttle\n            .throttle(refresh_interval, time, move |state| {\n                let meta = DrmEventMetadata {\n                    sequence: meta.sequence,\n                    time: DrmEventTime::Monotonic(Duration::ZERO),\n                };\n\n                let tty = state.backend.tty();\n                tty.on_vblank(&mut state.niri, node, crtc, meta);\n            })\n        {\n            // Throttled.\n            return;\n        }\n\n        let redraw_needed = match mem::replace(&mut output_state.redraw_state, RedrawState::Idle) {\n            RedrawState::WaitingForVBlank { redraw_needed } => redraw_needed,\n            state @ (RedrawState::Idle\n            | RedrawState::Queued\n            | RedrawState::WaitingForEstimatedVBlank(_)\n            | RedrawState::WaitingForEstimatedVBlankAndQueued(_)) => {\n                // This is an error!() because it shouldn't happen, but on some systems it somehow\n                // does. Kernel sending rogue vblank events?\n                //\n                // https://github.com/niri-wm/niri/issues/556\n                // https://github.com/niri-wm/niri/issues/615\n                error!(\n                    \"unexpected redraw state for output {name} (should be WaitingForVBlank); \\\n                     can happen when resuming from sleep or powering on monitors: {state:?}\"\n                );\n                true\n            }\n        };\n\n        // Mark the last frame as submitted.\n        match surface.compositor.frame_submitted() {\n            Ok(Some((mut feedback, target_presentation_time))) => {\n                let refresh = match refresh_interval {\n                    Some(refresh) => {\n                        if output_state.frame_clock.vrr() {\n                            Refresh::Variable(refresh)\n                        } else {\n                            Refresh::Fixed(refresh)\n                        }\n                    }\n                    None => Refresh::Unknown,\n                };\n\n                // FIXME: ideally should be monotonically increasing for a surface.\n                let seq = meta.sequence as u64;\n                let mut flags = wp_presentation_feedback::Kind::Vsync\n                    | wp_presentation_feedback::Kind::HwCompletion;\n\n                if !presentation_time.is_zero() {\n                    flags.insert(wp_presentation_feedback::Kind::HwClock);\n                }\n\n                feedback.presented::<_, smithay::utils::Monotonic>(time, refresh, seq, flags);\n\n                if !presentation_time.is_zero() {\n                    let misprediction_s =\n                        presentation_time.as_secs_f64() - target_presentation_time.as_secs_f64();\n                    tracy_client::Client::running().unwrap().plot(\n                        surface.presentation_misprediction_plot_name,\n                        misprediction_s * 1000.,\n                    );\n                }\n            }\n            Ok(None) => (),\n            Err(err) => {\n                warn!(\"error marking frame as submitted: {err}\");\n            }\n        }\n\n        if let Some(last_sequence) = output_state.last_drm_sequence {\n            let delta = meta.sequence as f64 - last_sequence as f64;\n            tracy_client::Client::running()\n                .unwrap()\n                .plot(surface.sequence_delta_plot_name, delta);\n        }\n        output_state.last_drm_sequence = Some(meta.sequence);\n\n        output_state.frame_clock.presented(presentation_time);\n\n        if redraw_needed || output_state.unfinished_animations_remain {\n            let vblank_frame = tracy_client::Client::running()\n                .unwrap()\n                .non_continuous_frame(surface.vblank_frame_name);\n            surface.vblank_frame = Some(vblank_frame);\n\n            niri.queue_redraw(&output);\n        } else {\n            niri.send_frame_callbacks(&output);\n        }\n    }\n\n    fn on_estimated_vblank_timer(&self, niri: &mut Niri, output: Output) {\n        let span = tracy_client::span!(\"Tty::on_estimated_vblank_timer\");\n\n        let name = output.name();\n        span.emit_text(&name);\n\n        let Some(output_state) = niri.output_state.get_mut(&output) else {\n            error!(\"missing output state for {name}\");\n            return;\n        };\n\n        // We waited for the timer, now we can send frame callbacks again.\n        output_state.frame_callback_sequence = output_state.frame_callback_sequence.wrapping_add(1);\n\n        match mem::replace(&mut output_state.redraw_state, RedrawState::Idle) {\n            RedrawState::Idle => unreachable!(),\n            RedrawState::Queued => unreachable!(),\n            RedrawState::WaitingForVBlank { .. } => unreachable!(),\n            RedrawState::WaitingForEstimatedVBlank(_) => (),\n            // The timer fired just in front of a redraw.\n            RedrawState::WaitingForEstimatedVBlankAndQueued(_) => {\n                output_state.redraw_state = RedrawState::Queued;\n                return;\n            }\n        }\n\n        if output_state.unfinished_animations_remain {\n            niri.queue_redraw(&output);\n        } else {\n            niri.send_frame_callbacks(&output);\n        }\n    }\n\n    pub fn seat_name(&self) -> String {\n        self.session.seat()\n    }\n\n    pub fn with_primary_renderer<T>(\n        &mut self,\n        f: impl FnOnce(&mut GlesRenderer) -> T,\n    ) -> Option<T> {\n        let mut renderer = self\n            .gpu_manager\n            .single_renderer(&self.primary_render_node)\n            .ok()?;\n        Some(f(renderer.as_gles_renderer()))\n    }\n\n    pub fn render(\n        &mut self,\n        niri: &mut Niri,\n        output: &Output,\n        target_presentation_time: Duration,\n    ) -> RenderResult {\n        let span = tracy_client::span!(\"Tty::render\");\n\n        let mut rv = RenderResult::Skipped;\n\n        let tty_state: &TtyOutputState = output.user_data().get().unwrap();\n        let Some(device) = self.devices.get_mut(&tty_state.node) else {\n            error!(\"missing output device\");\n            return rv;\n        };\n\n        let Some(surface) = device.surfaces.get_mut(&tty_state.crtc) else {\n            error!(\"missing surface\");\n            return rv;\n        };\n\n        span.emit_text(&surface.name.connector);\n\n        if !device.drm.is_active() {\n            // This branch hits any time we try to render while the user had switched to a\n            // different VT, so don't print anything here.\n            return rv;\n        }\n\n        let mut renderer = match self.gpu_manager.renderer(\n            &self.primary_render_node,\n            &device.render_node.unwrap_or(self.primary_render_node),\n            surface.compositor.format(),\n        ) {\n            Ok(renderer) => renderer,\n            Err(err) => {\n                warn!(\"error creating renderer for primary GPU: {err:?}\");\n                return rv;\n            }\n        };\n\n        // Render the elements.\n        let mut elements =\n            niri.render::<TtyRenderer>(&mut renderer, output, true, RenderTarget::Output);\n\n        // Visualize the damage, if enabled.\n        if niri.debug_draw_damage {\n            let output_state = niri.output_state.get_mut(output).unwrap();\n            draw_damage(&mut output_state.debug_damage_tracker, &mut elements);\n        }\n\n        // Overlay planes are disabled by default as they cause weird performance issues on my\n        // system.\n        let flags = {\n            let debug = &self.config.borrow().debug;\n\n            let primary_scanout_flag = if debug.restrict_primary_scanout_to_matching_format {\n                FrameFlags::ALLOW_PRIMARY_PLANE_SCANOUT\n            } else {\n                FrameFlags::ALLOW_PRIMARY_PLANE_SCANOUT_ANY\n            };\n            let mut flags = primary_scanout_flag | FrameFlags::ALLOW_CURSOR_PLANE_SCANOUT;\n\n            if debug.enable_overlay_planes {\n                flags.insert(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);\n            }\n            if debug.disable_direct_scanout {\n                flags.remove(primary_scanout_flag);\n                flags.remove(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);\n            }\n            if debug.disable_cursor_plane {\n                flags.remove(FrameFlags::ALLOW_CURSOR_PLANE_SCANOUT);\n            }\n            if debug.skip_cursor_only_updates_during_vrr {\n                let output_state = niri.output_state.get(output).unwrap();\n                if output_state.frame_clock.vrr() {\n                    flags.insert(FrameFlags::SKIP_CURSOR_ONLY_UPDATES);\n                }\n            }\n\n            flags\n        };\n\n        // Hand them over to the DRM.\n        let drm_compositor = &mut surface.compositor;\n        match drm_compositor.render_frame::<_, _>(&mut renderer, &elements, [0.; 4], flags) {\n            Ok(res) => {\n                let needs_sync = res.needs_sync()\n                    || self\n                        .config\n                        .borrow()\n                        .debug\n                        .wait_for_frame_completion_before_queueing;\n                if needs_sync {\n                    if let PrimaryPlaneElement::Swapchain(element) = res.primary_element {\n                        let _span = tracy_client::span!(\"wait for completion\");\n                        if let Err(err) = element.sync.wait() {\n                            warn!(\"error waiting for frame completion: {err:?}\");\n                        }\n                    }\n                }\n\n                niri.update_primary_scanout_output(output, &res.states);\n                if let Some(dmabuf_feedback) = surface.dmabuf_feedback.as_ref() {\n                    niri.send_dmabuf_feedbacks(output, dmabuf_feedback, &res.states);\n                }\n\n                if !res.is_empty {\n                    let presentation_feedbacks =\n                        niri.take_presentation_feedbacks(output, &res.states);\n                    let data = (presentation_feedbacks, target_presentation_time);\n\n                    match drm_compositor.queue_frame(data) {\n                        Ok(()) => {\n                            let output_state = niri.output_state.get_mut(output).unwrap();\n                            let new_state = RedrawState::WaitingForVBlank {\n                                redraw_needed: false,\n                            };\n                            match mem::replace(&mut output_state.redraw_state, new_state) {\n                                RedrawState::Idle => unreachable!(),\n                                RedrawState::Queued => (),\n                                RedrawState::WaitingForVBlank { .. } => unreachable!(),\n                                RedrawState::WaitingForEstimatedVBlank(_) => unreachable!(),\n                                RedrawState::WaitingForEstimatedVBlankAndQueued(token) => {\n                                    niri.event_loop.remove(token);\n                                }\n                            };\n\n                            // We queued this frame successfully, so the current client buffers were\n                            // latched. We can send frame callbacks now, since a new client commit\n                            // will no longer overwrite this frame and will wait for a VBlank.\n                            output_state.frame_callback_sequence =\n                                output_state.frame_callback_sequence.wrapping_add(1);\n\n                            return RenderResult::Submitted;\n                        }\n                        Err(err) => {\n                            warn!(\"error queueing frame: {err}\");\n                        }\n                    }\n                } else {\n                    rv = RenderResult::NoDamage;\n                }\n            }\n            Err(err) => {\n                // Can fail if we switched to a different TTY.\n                warn!(\"error rendering frame: {err}\");\n            }\n        }\n\n        // We're not expecting a vblank right after this.\n        drop(surface.vblank_frame.take());\n\n        // Queue a timer to fire at the predicted vblank time.\n        queue_estimated_vblank_timer(niri, output.clone(), target_presentation_time);\n\n        rv\n    }\n\n    pub fn change_vt(&mut self, vt: i32) {\n        if let Err(err) = self.session.change_vt(vt) {\n            warn!(\"error changing VT: {err}\");\n        }\n    }\n\n    pub fn suspend(&self) {\n        #[cfg(feature = \"dbus\")]\n        if let Err(err) = suspend() {\n            warn!(\"error suspending: {err:?}\");\n        }\n    }\n\n    pub fn toggle_debug_tint(&mut self) {\n        self.debug_tint = !self.debug_tint;\n\n        for device in self.devices.values_mut() {\n            for surface in device.surfaces.values_mut() {\n                let compositor = &mut surface.compositor;\n\n                let mut flags = compositor.debug_flags();\n                flags.set(DebugFlags::TINT, self.debug_tint);\n                compositor.set_debug_flags(flags);\n            }\n        }\n    }\n\n    pub fn import_dmabuf(&mut self, dmabuf: &Dmabuf) -> bool {\n        let mut renderer = match self.gpu_manager.single_renderer(&self.primary_render_node) {\n            Ok(renderer) => renderer,\n            Err(err) => {\n                debug!(\"error creating renderer for primary GPU: {err:?}\");\n                return false;\n            }\n        };\n\n        match renderer.import_dmabuf(dmabuf, None) {\n            Ok(_texture) => {\n                dmabuf.set_node(Some(self.primary_render_node));\n                true\n            }\n            Err(err) => {\n                debug!(\"error importing dmabuf: {err:?}\");\n                false\n            }\n        }\n    }\n\n    pub fn early_import(&mut self, surface: &WlSurface) {\n        if let Err(err) = self.gpu_manager.early_import(\n            // We always render on the primary GPU.\n            self.primary_render_node,\n            surface,\n        ) {\n            warn!(\"error doing early import: {err:?}\");\n        }\n    }\n\n    pub fn get_gamma_size(&self, output: &Output) -> anyhow::Result<u32> {\n        let tty_state = output.user_data().get::<TtyOutputState>().unwrap();\n        let crtc = tty_state.crtc;\n\n        let device = self\n            .devices\n            .get(&tty_state.node)\n            .context(\"missing device\")?;\n\n        let surface = device.surfaces.get(&crtc).context(\"missing surface\")?;\n        if let Some(gamma_props) = &surface.gamma_props {\n            gamma_props.gamma_size(&device.drm)\n        } else {\n            let info = device\n                .drm\n                .get_crtc(crtc)\n                .context(\"error getting crtc info\")?;\n            Ok(info.gamma_length())\n        }\n    }\n\n    pub fn set_gamma(&mut self, output: &Output, ramp: Option<Vec<u16>>) -> anyhow::Result<()> {\n        let tty_state = output.user_data().get::<TtyOutputState>().unwrap();\n        let crtc = tty_state.crtc;\n\n        let device = self\n            .devices\n            .get_mut(&tty_state.node)\n            .context(\"missing device\")?;\n        let surface = device.surfaces.get_mut(&crtc).context(\"missing surface\")?;\n\n        // Cannot change properties while the device is inactive.\n        if !self.session.is_active() {\n            surface.pending_gamma_change = Some(ramp);\n            return Ok(());\n        }\n\n        let ramp = ramp.as_deref();\n        if let Some(gamma_props) = &mut surface.gamma_props {\n            gamma_props.set_gamma(&device.drm, ramp)\n        } else {\n            set_gamma_for_crtc(&device.drm, crtc, ramp)\n        }\n    }\n\n    fn refresh_ipc_outputs(&self, niri: &mut Niri) {\n        let _span = tracy_client::span!(\"Tty::refresh_ipc_outputs\");\n\n        let mut ipc_outputs = HashMap::new();\n        let disable_monitor_names = self.config.borrow().debug.disable_monitor_names;\n\n        for (node, device) in &self.devices {\n            for (connector, crtc) in device.drm_scanner.crtcs() {\n                let connector_name = format_connector_name(connector);\n                let physical_size = connector.size();\n                let output_name = device.known_crtc_name(&crtc, connector, disable_monitor_names);\n\n                let surface = device.surfaces.get(&crtc);\n                let current_crtc_mode = surface.map(|surface| surface.compositor.pending_mode());\n                let mut current_mode = None;\n                let mut is_custom_mode = false;\n\n                let mut modes: Vec<niri_ipc::Mode> = connector\n                    .modes()\n                    .iter()\n                    .filter(|m| !m.flags().contains(ModeFlags::INTERLACE))\n                    .enumerate()\n                    .map(|(idx, m)| {\n                        if Some(*m) == current_crtc_mode {\n                            current_mode = Some(idx);\n                        }\n\n                        niri_ipc::Mode {\n                            width: m.size().0,\n                            height: m.size().1,\n                            refresh_rate: Mode::from(*m).refresh as u32,\n                            is_preferred: m.mode_type().contains(ModeTypeFlags::PREFERRED),\n                        }\n                    })\n                    .collect();\n\n                if let Some(crtc_mode) = current_crtc_mode {\n                    // Custom mode\n                    if crtc_mode.mode_type().contains(ModeTypeFlags::USERDEF) {\n                        modes.insert(\n                            0,\n                            niri_ipc::Mode {\n                                width: crtc_mode.size().0,\n                                height: crtc_mode.size().1,\n                                refresh_rate: Mode::from(crtc_mode).refresh as u32,\n                                is_preferred: false,\n                            },\n                        );\n                        current_mode = Some(0);\n                        is_custom_mode = true;\n                    }\n\n                    if current_mode.is_none() {\n                        if crtc_mode.flags().contains(ModeFlags::INTERLACE) {\n                            warn!(\"connector mode list missing current mode (interlaced)\");\n                        } else {\n                            error!(\"connector mode list missing current mode\");\n                        }\n                    }\n                }\n\n                let vrr_supported = surface\n                    .map(|surface| {\n                        matches!(\n                            surface.compositor.vrr_supported(connector.handle()),\n                            Ok(VrrSupport::Supported | VrrSupport::RequiresModeset)\n                        )\n                    })\n                    .unwrap_or_else(|| {\n                        is_vrr_capable(&device.drm, connector.handle()) == Some(true)\n                    });\n                let vrr_enabled = surface.is_some_and(|surface| surface.compositor.vrr_enabled());\n\n                let logical = niri\n                    .global_space\n                    .outputs()\n                    .find(|output| {\n                        let tty_state: &TtyOutputState = output.user_data().get().unwrap();\n                        tty_state.node == *node && tty_state.crtc == crtc\n                    })\n                    .map(logical_output);\n\n                let id = device.known_crtcs.get(&crtc).map(|info| info.id);\n                let id = id.unwrap_or_else(|| {\n                    error!(\"crtc for connector {connector_name} missing from known\");\n                    OutputId::next()\n                });\n\n                let ipc_output = niri_ipc::Output {\n                    name: connector_name,\n                    make: output_name.make.unwrap_or_else(|| \"Unknown\".into()),\n                    model: output_name.model.unwrap_or_else(|| \"Unknown\".into()),\n                    serial: output_name.serial,\n                    physical_size,\n                    modes,\n                    current_mode,\n                    is_custom_mode,\n                    vrr_supported,\n                    vrr_enabled,\n                    logical,\n                };\n\n                ipc_outputs.insert(id, ipc_output);\n            }\n        }\n\n        let mut guard = self.ipc_outputs.lock().unwrap();\n        *guard = ipc_outputs;\n        niri.ipc_outputs_changed = true;\n    }\n\n    pub fn ipc_outputs(&self) -> Arc<Mutex<IpcOutputMap>> {\n        self.ipc_outputs.clone()\n    }\n\n    #[cfg(feature = \"xdp-gnome-screencast\")]\n    pub fn primary_gbm_device(&self) -> Option<GbmDevice<DrmDeviceFd>> {\n        // Try to find a device corresponding to the primary render node.\n        let device = self\n            .devices\n            .values()\n            .find(|d| d.render_node == Some(self.primary_render_node));\n        // Otherwise, try to get the device corresponding to the primary node.\n        let device = device.or_else(|| self.devices.get(&self.primary_node));\n\n        Some(device?.gbm.clone())\n    }\n\n    pub fn set_monitors_active(&mut self, active: bool) {\n        // We only disable the CRTC here, this will also reset the\n        // surface state so that the next call to `render_frame` will\n        // always produce a new frame and `queue_frame` will change\n        // the CRTC to active. This makes sure we always enable a CRTC\n        // within an atomic operation.\n        if active {\n            return;\n        }\n\n        for device in self.devices.values_mut() {\n            for surface in device.surfaces.values_mut() {\n                if let Err(err) = surface.compositor.clear() {\n                    warn!(\"error clearing drm surface: {err:?}\");\n                }\n            }\n        }\n    }\n\n    pub fn set_output_on_demand_vrr(&mut self, niri: &mut Niri, output: &Output, enable_vrr: bool) {\n        let _span = tracy_client::span!(\"Tty::set_output_on_demand_vrr\");\n\n        let output_state = niri.output_state.get_mut(output).unwrap();\n        output_state.on_demand_vrr_enabled = enable_vrr;\n        if output_state.frame_clock.vrr() == enable_vrr {\n            return;\n        }\n        for (&node, device) in self.devices.iter_mut() {\n            for (&crtc, surface) in device.surfaces.iter_mut() {\n                let tty_state: &TtyOutputState = output.user_data().get().unwrap();\n                if tty_state.node == node && tty_state.crtc == crtc {\n                    let word = if enable_vrr { \"enabling\" } else { \"disabling\" };\n                    if let Err(err) = surface.compositor.use_vrr(enable_vrr) {\n                        warn!(\n                            \"output {:?}: error {} VRR: {err:?}\",\n                            surface.name.connector, word\n                        );\n                    }\n                    output_state\n                        .frame_clock\n                        .set_vrr(surface.compositor.vrr_enabled());\n\n                    self.refresh_ipc_outputs(niri);\n                    return;\n                }\n            }\n        }\n    }\n\n    pub fn update_ignored_nodes_config(&mut self, niri: &mut Niri) {\n        let _span = tracy_client::span!(\"Tty::update_ignored_nodes_config\");\n\n        // If we're inactive, we can't do anything, so just set a flag for later.\n        if !self.session.is_active() {\n            self.update_ignored_nodes_on_resume = true;\n            return;\n        }\n\n        let mut ignored_nodes = ignored_nodes_from_config(&self.config.borrow());\n        if ignored_nodes.remove(&self.primary_node)\n            || ignored_nodes.remove(&self.primary_render_node)\n        {\n            warn!(\"ignoring the primary node or render node is not allowed\");\n        }\n\n        if ignored_nodes == self.ignored_nodes {\n            return;\n        }\n        self.ignored_nodes = ignored_nodes;\n\n        let mut device_list = self\n            .udev_dispatcher\n            .as_source_ref()\n            .device_list()\n            .map(|(device_id, path)| (device_id, path.to_owned()))\n            .collect::<HashMap<_, _>>();\n\n        let removed_devices = self\n            .devices\n            .keys()\n            .filter(|node| {\n                self.ignored_nodes.contains(node) || !device_list.contains_key(&node.dev_id())\n            })\n            .copied()\n            .collect::<Vec<_>>();\n\n        for node in removed_devices {\n            device_list.remove(&node.dev_id());\n            self.device_removed(node.dev_id(), niri);\n        }\n\n        for node in self.devices.keys() {\n            device_list.remove(&node.dev_id());\n        }\n\n        for (device_id, path) in device_list {\n            if let Err(err) = self.device_added(device_id, &path, niri) {\n                warn!(\"error adding device {path:?}: {err:?}\");\n            }\n        }\n    }\n\n    fn should_disable_laptop_panels(&self, is_lid_closed: bool) -> bool {\n        if !is_lid_closed {\n            return false;\n        }\n\n        let config = self.config.borrow();\n        if !config.debug.keep_laptop_panel_on_when_lid_is_closed {\n            // Check if any external monitor is connected.\n            for device in self.devices.values() {\n                for (connector, _crtc) in device.drm_scanner.crtcs() {\n                    if !is_laptop_panel(&format_connector_name(connector)) {\n                        return true;\n                    }\n                }\n            }\n        }\n\n        false\n    }\n\n    pub fn on_output_config_changed(&mut self, niri: &mut Niri) {\n        let _span = tracy_client::span!(\"Tty::on_output_config_changed\");\n\n        // If we're inactive, we can't do anything, so just set a flag for later.\n        if !self.session.is_active() {\n            self.update_output_config_on_resume = true;\n            return;\n        }\n        self.update_output_config_on_resume = false;\n\n        // Figure out if we should disable laptop panels.\n        let disable_laptop_panels = self.should_disable_laptop_panels(niri.is_lid_closed);\n        let should_disable = |connector: &str| disable_laptop_panels && is_laptop_panel(connector);\n\n        let mut to_disconnect = vec![];\n        let mut to_connect = vec![];\n\n        for (&node, device) in &mut self.devices {\n            for (&crtc, surface) in device.surfaces.iter_mut() {\n                let config = self\n                    .config\n                    .borrow()\n                    .outputs\n                    .find(&surface.name)\n                    .cloned()\n                    .unwrap_or_default();\n                if config.off || should_disable(&surface.name.connector) {\n                    to_disconnect.push((node, crtc));\n                    continue;\n                }\n\n                // Check if we need to change the mode.\n                let Some(connector) = device.drm_scanner.connectors().get(&surface.connector)\n                else {\n                    error!(\"missing enabled connector in drm_scanner\");\n                    continue;\n                };\n\n                let mut mode = None;\n                if let Some(modeline) = &config.modeline {\n                    match calculate_drm_mode_from_modeline(modeline) {\n                        Ok(x) => mode = Some(x),\n                        Err(err) => {\n                            warn!(\n                                \"output {:?}: invalid custom modeline; \\\n                                 falling back to advertised modes: {err:?}\",\n                                surface.name.connector\n                            );\n                        }\n                    }\n                }\n\n                let (mode, fallback) = match mode {\n                    Some(x) => (x, false),\n                    None => match pick_mode(connector, config.mode) {\n                        Some(result) => result,\n                        None => {\n                            warn!(\"couldn't pick mode for enabled connector\");\n                            continue;\n                        }\n                    },\n                };\n\n                let change_mode = surface.compositor.pending_mode() != mode;\n\n                let vrr_enabled = surface.compositor.vrr_enabled();\n                let change_always_vrr = vrr_enabled != config.is_vrr_always_on();\n                let is_on_demand_vrr = config.is_vrr_on_demand();\n\n                if !change_mode && !change_always_vrr && !is_on_demand_vrr {\n                    continue;\n                }\n\n                let output = niri\n                    .global_space\n                    .outputs()\n                    .find(|output| {\n                        let tty_state: &TtyOutputState = output.user_data().get().unwrap();\n                        tty_state.node == node && tty_state.crtc == crtc\n                    })\n                    .cloned();\n                let Some(output) = output else {\n                    error!(\"missing output for crtc: {crtc:?}\");\n                    continue;\n                };\n                let Some(output_state) = niri.output_state.get_mut(&output) else {\n                    error!(\"missing state for output {:?}\", surface.name.connector);\n                    continue;\n                };\n\n                if (is_on_demand_vrr && vrr_enabled != output_state.on_demand_vrr_enabled)\n                    || (!is_on_demand_vrr && change_always_vrr)\n                {\n                    let vrr = !vrr_enabled;\n                    let word = if vrr { \"enabling\" } else { \"disabling\" };\n                    if let Err(err) = surface.compositor.use_vrr(vrr) {\n                        warn!(\n                            \"output {:?}: error {} VRR: {err:?}\",\n                            surface.name.connector, word\n                        );\n                    }\n                    output_state\n                        .frame_clock\n                        .set_vrr(surface.compositor.vrr_enabled());\n                }\n\n                if change_mode {\n                    if fallback {\n                        let target = config.mode.unwrap();\n                        warn!(\n                            \"output {:?}: configured mode {}x{}{} could not be found, \\\n                             falling back to preferred\",\n                            surface.name.connector,\n                            target.mode.width,\n                            target.mode.height,\n                            if let Some(refresh) = target.mode.refresh {\n                                format!(\"@{refresh}\")\n                            } else {\n                                String::new()\n                            },\n                        );\n                    }\n\n                    debug!(\n                        \"output {:?}: picking mode: {mode:?}\",\n                        surface.name.connector\n                    );\n                    if let Err(err) = surface.compositor.use_mode(mode) {\n                        warn!(\"error changing mode: {err:?}\");\n                        continue;\n                    }\n\n                    let wl_mode = Mode::from(mode);\n                    output.change_current_state(Some(wl_mode), None, None, None);\n                    output.set_preferred(wl_mode);\n                    output_state.frame_clock = FrameClock::new(\n                        Some(refresh_interval(mode)),\n                        surface.compositor.vrr_enabled(),\n                    );\n                    niri.output_resized(&output);\n                }\n            }\n\n            let config = self.config.borrow();\n            let disable_monitor_names = config.debug.disable_monitor_names;\n\n            for (connector, crtc) in device.drm_scanner.crtcs() {\n                // Check if connected.\n                if connector.state() != connector::State::Connected {\n                    continue;\n                }\n\n                // Check if already enabled.\n                if device.surfaces.contains_key(&crtc)\n                    || device\n                        .non_desktop_connectors\n                        .contains(&(connector.handle(), crtc))\n                {\n                    continue;\n                }\n\n                let output_name = device.known_crtc_name(&crtc, connector, disable_monitor_names);\n\n                let config = config\n                    .outputs\n                    .find(&output_name)\n                    .cloned()\n                    .unwrap_or_default();\n\n                if !(config.off || should_disable(&output_name.connector)) {\n                    to_connect.push((node, connector.clone(), crtc, output_name));\n                }\n            }\n        }\n\n        for (node, crtc) in to_disconnect {\n            self.connector_disconnected(niri, node, crtc);\n        }\n\n        // Sort by output name to get more predictable first focused output at initial compositor\n        // startup, when multiple connectors appear at once.\n        to_connect.sort_unstable_by(|a, b| a.3.compare(&b.3));\n\n        for (node, connector, crtc, _name) in to_connect {\n            if let Err(err) = self.connector_connected(niri, node, connector, crtc) {\n                warn!(\"error connecting connector: {err:?}\");\n            }\n        }\n\n        self.refresh_ipc_outputs(niri);\n    }\n\n    pub fn get_device_from_node(&mut self, node: DrmNode) -> Option<&mut OutputDevice> {\n        self.devices.get_mut(&node)\n    }\n\n    pub fn disconnected_connector_name_by_name_match(&self, target: &str) -> Option<OutputName> {\n        let disable_monitor_names = self.config.borrow().debug.disable_monitor_names;\n        for device in self.devices.values() {\n            for (connector, crtc) in device.drm_scanner.crtcs() {\n                // Check if connected.\n                if connector.state() != connector::State::Connected {\n                    continue;\n                }\n\n                // Check if already enabled.\n                if device.surfaces.contains_key(&crtc)\n                    || device\n                        .non_desktop_connectors\n                        .contains(&(connector.handle(), crtc))\n                {\n                    continue;\n                }\n\n                let output_name = device.known_crtc_name(&crtc, connector, disable_monitor_names);\n                if output_name.matches(target) {\n                    return Some(output_name);\n                }\n            }\n        }\n\n        None\n    }\n}\n\nimpl GammaProps {\n    fn new(device: &DrmDevice, crtc: crtc::Handle) -> anyhow::Result<Self> {\n        let mut gamma_lut = None;\n        let mut gamma_lut_size = None;\n\n        let props = device\n            .get_properties(crtc)\n            .context(\"error getting properties\")?;\n        for (prop, _) in props {\n            let Ok(info) = device.get_property(prop) else {\n                continue;\n            };\n\n            let Ok(name) = info.name().to_str() else {\n                continue;\n            };\n\n            match name {\n                \"GAMMA_LUT\" => {\n                    ensure!(\n                        matches!(info.value_type(), property::ValueType::Blob),\n                        \"wrong GAMMA_LUT value type\"\n                    );\n                    gamma_lut = Some(prop);\n                }\n                \"GAMMA_LUT_SIZE\" => {\n                    ensure!(\n                        matches!(info.value_type(), property::ValueType::UnsignedRange(_, _)),\n                        \"wrong GAMMA_LUT_SIZE value type\"\n                    );\n                    gamma_lut_size = Some(prop);\n                }\n                _ => (),\n            }\n        }\n\n        let gamma_lut = gamma_lut.context(\"missing GAMMA_LUT property\")?;\n        let gamma_lut_size = gamma_lut_size.context(\"missing GAMMA_LUT_SIZE property\")?;\n\n        Ok(Self {\n            crtc,\n            gamma_lut,\n            gamma_lut_size,\n            previous_blob: None,\n        })\n    }\n\n    fn gamma_size(&self, device: &DrmDevice) -> anyhow::Result<u32> {\n        let value = get_drm_property(device, self.crtc, self.gamma_lut_size)\n            .context(\"missing GAMMA_LUT_SIZE property\")?;\n        Ok(value as u32)\n    }\n\n    fn set_gamma(&mut self, device: &DrmDevice, gamma: Option<&[u16]>) -> anyhow::Result<()> {\n        let _span = tracy_client::span!(\"GammaProps::set_gamma\");\n\n        let blob = if let Some(gamma) = gamma {\n            let gamma_size = self\n                .gamma_size(device)\n                .context(\"error getting gamma size\")? as usize;\n\n            ensure!(gamma.len() == gamma_size * 3, \"wrong gamma length\");\n\n            #[allow(non_camel_case_types)]\n            #[repr(C)]\n            #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]\n            pub struct drm_color_lut {\n                pub red: u16,\n                pub green: u16,\n                pub blue: u16,\n                pub reserved: u16,\n            }\n\n            let (red, rest) = gamma.split_at(gamma_size);\n            let (blue, green) = rest.split_at(gamma_size);\n            let mut data = zip(zip(red, blue), green)\n                .map(|((&red, &green), &blue)| drm_color_lut {\n                    red,\n                    green,\n                    blue,\n                    reserved: 0,\n                })\n                .collect::<Vec<_>>();\n            let data = cast_slice_mut(&mut data);\n\n            let blob = drm_ffi::mode::create_property_blob(device.as_fd(), data)\n                .context(\"error creating property blob\")?;\n            NonZeroU64::new(u64::from(blob.blob_id))\n        } else {\n            None\n        };\n\n        {\n            let _span = tracy_client::span!(\"set_property\");\n\n            let blob = blob.map(NonZeroU64::get).unwrap_or(0);\n            device\n                .set_property(\n                    self.crtc,\n                    self.gamma_lut,\n                    property::Value::Blob(blob).into(),\n                )\n                .context(\"error setting GAMMA_LUT\")\n                .inspect_err(|_| {\n                    if blob != 0 {\n                        // Destroy the blob we just allocated.\n                        if let Err(err) = device.destroy_property_blob(blob) {\n                            warn!(\"error destroying GAMMA_LUT property blob: {err:?}\");\n                        }\n                    }\n                })?;\n        }\n\n        if let Some(blob) = mem::replace(&mut self.previous_blob, blob) {\n            if let Err(err) = device.destroy_property_blob(blob.get()) {\n                warn!(\"error destroying previous GAMMA_LUT blob: {err:?}\");\n            }\n        }\n\n        Ok(())\n    }\n\n    fn restore_gamma(&self, device: &DrmDevice) -> anyhow::Result<()> {\n        let _span = tracy_client::span!(\"GammaProps::restore_gamma\");\n\n        let blob = self.previous_blob.map(NonZeroU64::get).unwrap_or(0);\n        device\n            .set_property(\n                self.crtc,\n                self.gamma_lut,\n                property::Value::Blob(blob).into(),\n            )\n            .context(\"error setting GAMMA_LUT\")?;\n\n        Ok(())\n    }\n}\n\nfn primary_node_from_render_node(path: &Path) -> Option<(DrmNode, DrmNode)> {\n    match DrmNode::from_path(path) {\n        Ok(node) => {\n            if node.ty() == NodeType::Render {\n                match node.node_with_type(NodeType::Primary) {\n                    Some(Ok(primary_node)) => {\n                        return Some((primary_node, node));\n                    }\n                    Some(Err(err)) => {\n                        warn!(\"error opening primary node for render node {path:?}: {err:?}\");\n                    }\n                    None => {\n                        warn!(\"error opening primary node for render node {path:?}\");\n                    }\n                }\n            } else {\n                warn!(\"DRM node {path:?} is not a render node\");\n\n                // Gracefully handle misconfiguration on regular desktop systems.\n                if let Some(Ok(render_node)) = node.node_with_type(NodeType::Render) {\n                    return Some((node, render_node));\n                }\n\n                warn!(\"could not get render node for DRM node {path:?}; proceeding anyway\");\n                return Some((node, node));\n            }\n        }\n        Err(err) => {\n            warn!(\"error opening {path:?} as DRM node: {err:?}\");\n        }\n    }\n\n    None\n}\n\nfn primary_node_from_config(config: &Config) -> Option<(DrmNode, DrmNode)> {\n    let path = config.debug.render_drm_device.as_ref()?;\n    debug!(\"attempting to use render node from config: {path:?}\");\n\n    primary_node_from_render_node(path)\n}\n\nfn ignored_nodes_from_config(config: &Config) -> HashSet<DrmNode> {\n    let mut disabled_nodes = HashSet::new();\n\n    for path in &config.debug.ignored_drm_devices {\n        if let Some((primary_node, render_node)) = primary_node_from_render_node(path) {\n            disabled_nodes.insert(primary_node);\n            disabled_nodes.insert(render_node);\n        }\n    }\n\n    disabled_nodes\n}\n\nfn surface_dmabuf_feedback(\n    compositor: &GbmDrmCompositor,\n    primary_formats: FormatSet,\n    primary_render_node: DrmNode,\n    surface_render_node: Option<DrmNode>,\n    surface_scanout_node: DrmNode,\n) -> Result<SurfaceDmabufFeedback, io::Error> {\n    let surface = compositor.surface();\n    let planes = surface.planes();\n\n    let primary_plane_formats = surface.plane_info().formats.clone();\n    let primary_or_overlay_plane_formats = primary_plane_formats\n        .iter()\n        .chain(planes.overlay.iter().flat_map(|p| p.formats.iter()))\n        .copied()\n        .collect::<FormatSet>();\n\n    // We limit the scan-out trache to formats we can also render from so that there is always a\n    // fallback render path available in case the supplied buffer can not be scanned out directly.\n    let mut primary_scanout_formats = primary_plane_formats\n        .intersection(&primary_formats)\n        .copied()\n        .collect::<Vec<_>>();\n    let mut primary_or_overlay_scanout_formats = primary_or_overlay_plane_formats\n        .intersection(&primary_formats)\n        .copied()\n        .collect::<Vec<_>>();\n\n    // HACK: AMD iGPU + dGPU systems share some modifiers between the two, and yet cross-device\n    // buffers produce a glitched scanout if the modifier is not Linear...\n    //\n    // Also limit scan-out formats to Linear if we have a device without a render node (i.e.\n    // we're rendering on a different device).\n    if surface_render_node != Some(primary_render_node) {\n        primary_scanout_formats.retain(|f| f.modifier == Modifier::Linear);\n        primary_or_overlay_scanout_formats.retain(|f| f.modifier == Modifier::Linear);\n    }\n\n    let builder = DmabufFeedbackBuilder::new(primary_render_node.dev_id(), primary_formats);\n\n    trace!(\n        \"primary scanout formats: {}, overlay adds: {}\",\n        primary_scanout_formats.len(),\n        primary_or_overlay_scanout_formats.len() - primary_scanout_formats.len(),\n    );\n\n    // Prefer the primary-plane-only formats, then primary-or-overlay-plane formats. This will\n    // increase the chance of scanning out a client even with our disabled-by-default overlay\n    // planes.\n    let scanout = builder\n        .clone()\n        .add_preference_tranche(\n            surface_scanout_node.dev_id(),\n            Some(TrancheFlags::Scanout),\n            primary_scanout_formats,\n        )\n        .add_preference_tranche(\n            surface_scanout_node.dev_id(),\n            Some(TrancheFlags::Scanout),\n            primary_or_overlay_scanout_formats,\n        )\n        .build()?;\n\n    // If this is the primary node surface, send scanout formats in both tranches to avoid\n    // duplication.\n    let render = if surface_render_node == Some(primary_render_node) {\n        scanout.clone()\n    } else {\n        builder.build()?\n    };\n\n    Ok(SurfaceDmabufFeedback { render, scanout })\n}\n\nfn find_drm_property(\n    drm: &DrmDevice,\n    resource: impl ResourceHandle,\n    name: &str,\n) -> Option<(property::Handle, property::Info, property::RawValue)> {\n    let props = match drm.get_properties(resource) {\n        Ok(props) => props,\n        Err(err) => {\n            warn!(\"error getting properties: {err:?}\");\n            return None;\n        }\n    };\n\n    props.into_iter().find_map(|(handle, value)| {\n        let info = drm.get_property(handle).ok()?;\n        let n = info.name().to_str().ok()?;\n\n        (n == name).then_some((handle, info, value))\n    })\n}\n\nfn get_drm_property(\n    drm: &DrmDevice,\n    resource: impl ResourceHandle,\n    prop: property::Handle,\n) -> Option<property::RawValue> {\n    let props = match drm.get_properties(resource) {\n        Ok(props) => props,\n        Err(err) => {\n            warn!(\"error getting properties: {err:?}\");\n            return None;\n        }\n    };\n\n    props\n        .into_iter()\n        .find_map(|(handle, value)| (handle == prop).then_some(value))\n}\n\nfn refresh_interval(mode: DrmMode) -> Duration {\n    let clock = mode.clock() as u64;\n    let htotal = mode.hsync().2 as u64;\n    let vtotal = mode.vsync().2 as u64;\n\n    let mut numerator = htotal * vtotal * 1_000_000;\n    let mut denominator = clock;\n\n    if mode.flags().contains(ModeFlags::INTERLACE) {\n        denominator *= 2;\n    }\n\n    if mode.flags().contains(ModeFlags::DBLSCAN) {\n        numerator *= 2;\n    }\n\n    if mode.vscan() > 1 {\n        numerator *= mode.vscan() as u64;\n    }\n\n    let refresh_interval = (numerator + denominator / 2) / denominator;\n    Duration::from_nanos(refresh_interval)\n}\n\n#[cfg(feature = \"dbus\")]\nfn suspend() -> anyhow::Result<()> {\n    let conn = zbus::blocking::Connection::system().context(\"error connecting to system bus\")?;\n\n    conn.call_method(\n        Some(\"org.freedesktop.login1\"),\n        \"/org/freedesktop/login1\",\n        Some(\"org.freedesktop.login1.Manager\"),\n        \"Suspend\",\n        &(true),\n    )\n    .context(\"error suspending\")?;\n\n    Ok(())\n}\n\nfn queue_estimated_vblank_timer(\n    niri: &mut Niri,\n    output: Output,\n    target_presentation_time: Duration,\n) {\n    let output_state = niri.output_state.get_mut(&output).unwrap();\n    match mem::take(&mut output_state.redraw_state) {\n        RedrawState::Idle => unreachable!(),\n        RedrawState::Queued => (),\n        RedrawState::WaitingForVBlank { .. } => unreachable!(),\n        RedrawState::WaitingForEstimatedVBlank(token)\n        | RedrawState::WaitingForEstimatedVBlankAndQueued(token) => {\n            output_state.redraw_state = RedrawState::WaitingForEstimatedVBlank(token);\n            return;\n        }\n    }\n\n    let now = get_monotonic_time();\n    let mut duration = target_presentation_time.saturating_sub(now);\n\n    // No use setting a zero timer, since we'll send frame callbacks anyway right after the call to\n    // render(). This can happen for example with unknown presentation time from DRM.\n    if duration.is_zero() {\n        duration += output_state\n            .frame_clock\n            .refresh_interval()\n            // Unknown refresh interval, i.e. winit backend. Would be good to estimate it somehow\n            // but it's not that important for this code path.\n            .unwrap_or(Duration::from_micros(16_667));\n    }\n\n    trace!(\"queueing estimated vblank timer to fire in {duration:?}\");\n\n    let timer = Timer::from_duration(duration);\n    let token = niri\n        .event_loop\n        .insert_source(timer, move |_, _, data| {\n            data.backend\n                .tty()\n                .on_estimated_vblank_timer(&mut data.niri, output.clone());\n            TimeoutAction::Drop\n        })\n        .unwrap();\n    output_state.redraw_state = RedrawState::WaitingForEstimatedVBlank(token);\n}\n\npub fn calculate_drm_mode_from_modeline(modeline: &Modeline) -> anyhow::Result<DrmMode> {\n    ensure!(\n        modeline.hdisplay < modeline.hsync_start,\n        \"hdisplay {} must be < hsync_start {}\",\n        modeline.hdisplay,\n        modeline.hsync_start\n    );\n    ensure!(\n        modeline.hsync_start < modeline.hsync_end,\n        \"hsync_start {} must be < hsync_end {}\",\n        modeline.hsync_start,\n        modeline.hsync_end\n    );\n    ensure!(\n        modeline.hsync_end < modeline.htotal,\n        \"hsync_end {} must be < htotal {}\",\n        modeline.hsync_end,\n        modeline.htotal\n    );\n    ensure!(\n        modeline.vdisplay < modeline.vsync_start,\n        \"vdisplay {} must be < vsync_start {}\",\n        modeline.vdisplay,\n        modeline.vsync_start\n    );\n    ensure!(\n        modeline.vsync_start < modeline.vsync_end,\n        \"vsync_start {} must be < vsync_end {}\",\n        modeline.vsync_start,\n        modeline.vsync_end\n    );\n    ensure!(\n        modeline.vsync_end < modeline.vtotal,\n        \"vsync_end {} must be < vtotal {}\",\n        modeline.vsync_end,\n        modeline.vtotal\n    );\n\n    let pixel_clock_kilo_hertz = modeline.clock * 1000.0;\n    // Calculated as documented in the CVT 1.2 standard:\n    // https://app.box.com/s/vcocw3z73ta09txiskj7cnk6289j356b/file/93518784646\n    let vrefresh_hertz = (pixel_clock_kilo_hertz * 1000.0)\n        / (modeline.htotal as u64 * modeline.vtotal as u64) as f64;\n    ensure!(\n        vrefresh_hertz.is_finite(),\n        \"calculated refresh rate is not finite\"\n    );\n    let vrefresh_rounded = vrefresh_hertz.round() as u32;\n\n    let flags = match modeline.hsync_polarity {\n        HSyncPolarity::PHSync => ModeFlags::PHSYNC,\n        HSyncPolarity::NHSync => ModeFlags::NHSYNC,\n    } | match modeline.vsync_polarity {\n        VSyncPolarity::PVSync => ModeFlags::PVSYNC,\n        VSyncPolarity::NVSync => ModeFlags::NVSYNC,\n    };\n\n    let mode_name = format!(\n        \"{}x{}@{:.2}\",\n        modeline.hdisplay, modeline.vdisplay, vrefresh_hertz\n    );\n    let name = modeinfo_name_slice_from_string(&mode_name);\n\n    // https://www.kernel.org/doc/html/v6.17/gpu/drm-uapi.html#c.drm_mode_modeinfo\n    Ok(DrmMode::from(drm_mode_modeinfo {\n        clock: pixel_clock_kilo_hertz.round() as u32,\n        hdisplay: modeline.hdisplay,\n        hsync_start: modeline.hsync_start,\n        hsync_end: modeline.hsync_end,\n        htotal: modeline.htotal,\n        vdisplay: modeline.vdisplay,\n        vsync_start: modeline.vsync_start,\n        vsync_end: modeline.vsync_end,\n        vtotal: modeline.vtotal,\n        vrefresh: vrefresh_rounded,\n        flags: flags.bits(),\n        name,\n        // Defaults\n        type_: drm_ffi::DRM_MODE_TYPE_USERDEF,\n        hskew: 0,\n        vscan: 0,\n    }))\n}\n\npub fn calculate_mode_cvt(width: u16, height: u16, refresh: f64) -> DrmMode {\n    // Cross-checked with sway's implementation:\n    // https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/22528542970687720556035790212df8d9bb30bb/backend/drm/util.c#L251\n\n    let options = libdisplay_info::cvt::Options {\n        red_blank_ver: libdisplay_info::cvt::ReducedBlankingVersion::None,\n        h_pixels: width as i32,\n        v_lines: height as i32,\n        ip_freq_rqd: refresh,\n\n        // Defaults\n        video_opt: false,\n        vblank: 0f64,\n        additional_hblank: 0,\n        early_vsync_rqd: false,\n        int_rqd: false,\n        margins_rqd: false,\n    };\n    let cvt_timing = libdisplay_info::cvt::Timing::compute(options);\n\n    let hsync_start = width + cvt_timing.h_front_porch as u16;\n    let vsync_start = (cvt_timing.v_lines_rnd + cvt_timing.v_front_porch) as u16;\n    let hsync_end = hsync_start + cvt_timing.h_sync as u16;\n    let vsync_end = vsync_start + cvt_timing.v_sync as u16;\n\n    let htotal = hsync_end + cvt_timing.h_back_porch as u16;\n    let vtotal = vsync_end + cvt_timing.v_back_porch as u16;\n\n    let clock = f64::round(cvt_timing.act_pixel_freq * 1000f64) as u32;\n    let vrefresh = f64::round(cvt_timing.act_frame_rate) as u32;\n\n    let flags = drm_ffi::DRM_MODE_FLAG_NHSYNC | drm_ffi::DRM_MODE_FLAG_PVSYNC;\n\n    let mode_name = format!(\"{width}x{height}@{:.2}\", cvt_timing.act_frame_rate);\n    let name = modeinfo_name_slice_from_string(&mode_name);\n\n    let drm_ffi_mode = drm_ffi::drm_sys::drm_mode_modeinfo {\n        clock,\n\n        hdisplay: width,\n        hsync_start,\n        hsync_end,\n        htotal,\n\n        vdisplay: height,\n        vsync_start,\n        vsync_end,\n        vtotal,\n\n        vrefresh,\n\n        flags,\n        type_: drm_ffi::DRM_MODE_TYPE_USERDEF,\n        name,\n\n        // Defaults\n        hskew: 0,\n        vscan: 0,\n    };\n\n    DrmMode::from(drm_ffi_mode)\n}\n\n// Returns a c-string of maximally 31 Rust string chars + null terminator. Excess characters are\n// dropped.\nfn modeinfo_name_slice_from_string(mode_name: &str) -> [core::ffi::c_char; 32] {\n    let mut name: [core::ffi::c_char; 32] = [0; 32];\n\n    for (a, b) in zip(&mut name[..31], mode_name.as_bytes()) {\n        // Can be u8 on aarch64 and i8 on x86_64.\n        *a = *b as _;\n    }\n\n    name\n}\n\nfn pick_mode(\n    connector: &connector::Info,\n    target: Option<niri_config::output::Mode>,\n) -> Option<(control::Mode, bool)> {\n    let mut mode = None;\n    let mut fallback = false;\n\n    if let Some(target) = target {\n        let target_mode = target.mode;\n\n        if target.custom {\n            if let Some(refresh) = target_mode.refresh {\n                let custom_mode =\n                    calculate_mode_cvt(target_mode.width, target_mode.height, refresh);\n                return Some((custom_mode, false));\n            } else {\n                warn!(\"ignoring custom mode without refresh rate\");\n            }\n        }\n\n        let refresh = target_mode.refresh.map(|r| (r * 1000.).round() as i32);\n        for m in connector.modes() {\n            if m.size() != (target.mode.width, target.mode.height) {\n                continue;\n            }\n\n            // Interlaced modes don't appear to work.\n            if m.flags().contains(ModeFlags::INTERLACE) {\n                continue;\n            }\n\n            if let Some(refresh) = refresh {\n                // If refresh is set, only pick modes with matching refresh.\n                let wl_mode = Mode::from(*m);\n                if wl_mode.refresh == refresh {\n                    mode = Some(m);\n                }\n            } else if let Some(curr) = mode {\n                // If refresh isn't set, pick the mode with the highest refresh.\n                if curr.vrefresh() < m.vrefresh() {\n                    mode = Some(m);\n                }\n            } else {\n                mode = Some(m);\n            }\n        }\n\n        if mode.is_none() {\n            fallback = true;\n        }\n    }\n\n    if mode.is_none() {\n        // Pick a preferred mode.\n        for m in connector.modes() {\n            if !m.mode_type().contains(ModeTypeFlags::PREFERRED) {\n                continue;\n            }\n\n            if let Some(curr) = mode {\n                if curr.vrefresh() < m.vrefresh() {\n                    mode = Some(m);\n                }\n            } else {\n                mode = Some(m);\n            }\n        }\n    }\n\n    if mode.is_none() {\n        // Last attempt.\n        mode = connector.modes().first();\n    }\n\n    mode.map(|m| (*m, fallback))\n}\n\nfn get_edid_info(\n    device: &DrmDevice,\n    connector: connector::Handle,\n) -> anyhow::Result<libdisplay_info::info::Info> {\n    let (_, info, value) =\n        find_drm_property(device, connector, \"EDID\").context(\"no EDID property\")?;\n    let blob = info\n        .value_type()\n        .convert_value(value)\n        .as_blob()\n        .context(\"EDID was not blob type\")?;\n    let data = device\n        .get_property_blob(blob)\n        .context(\"error getting EDID blob value\")?;\n    libdisplay_info::info::Info::parse_edid(&data).context(\"error parsing EDID\")\n}\n\nimpl<'a> ConnectorProperties<'a> {\n    fn try_new(device: &'a DrmDevice, connector: connector::Handle) -> anyhow::Result<Self> {\n        let prop_vals = device\n            .get_properties(connector)\n            .context(\"error getting properties\")?;\n\n        let mut properties = Vec::new();\n\n        for (prop, value) in prop_vals {\n            let info = device\n                .get_property(prop)\n                .context(\"error getting property\")?;\n\n            properties.push((info, value));\n        }\n\n        Ok(Self {\n            device,\n            connector,\n            properties,\n        })\n    }\n\n    fn find(&self, name: &std::ffi::CStr) -> anyhow::Result<&(property::Info, property::RawValue)> {\n        for prop in &self.properties {\n            if prop.0.name() == name {\n                return Ok(prop);\n            }\n        }\n\n        Err(anyhow!(\"couldn't find property: {name:?}\"))\n    }\n}\n\nconst DRM_MODE_COLORIMETRY_DEFAULT: u64 = 0;\n\nfn reset_hdr(props: &ConnectorProperties) -> anyhow::Result<()> {\n    let (info, value) = props.find(c\"HDR_OUTPUT_METADATA\")?;\n    let property::ValueType::Blob = info.value_type() else {\n        bail!(\"wrong property type\")\n    };\n\n    if *value != 0 {\n        props\n            .device\n            .set_property(props.connector, info.handle(), 0)\n            .context(\"error setting property\")?;\n    }\n\n    let (info, value) = props.find(c\"Colorspace\")?;\n    let property::ValueType::Enum(_) = info.value_type() else {\n        bail!(\"wrong property type\")\n    };\n    if *value != DRM_MODE_COLORIMETRY_DEFAULT {\n        props\n            .device\n            .set_property(props.connector, info.handle(), DRM_MODE_COLORIMETRY_DEFAULT)\n            .context(\"error setting property\")?;\n    }\n\n    Ok(())\n}\n\nfn is_vrr_capable(device: &DrmDevice, connector: connector::Handle) -> Option<bool> {\n    let (_, info, value) = find_drm_property(device, connector, \"vrr_capable\")?;\n    info.value_type().convert_value(value).as_boolean()\n}\n\nfn get_panel_orientation(props: &ConnectorProperties) -> anyhow::Result<Transform> {\n    let (info, value) = props.find(c\"panel orientation\")?;\n    match info.value_type().convert_value(*value) {\n        property::Value::Enum(Some(val)) => match val.value() {\n            // \"Normal\"\n            0 => Ok(Transform::Normal),\n            // \"Upside Down\"\n            1 => Ok(Transform::_180),\n            // \"Left Side Up\"\n            2 => Ok(Transform::_90),\n            // \"Right Side Up\"\n            3 => Ok(Transform::_270),\n            _ => bail!(\"panel orientation has invalid value: {:?}\", val),\n        },\n        _ => bail!(\"panel orientation has wrong value type\"),\n    }\n}\n\npub fn set_gamma_for_crtc(\n    device: &DrmDevice,\n    crtc: crtc::Handle,\n    ramp: Option<&[u16]>,\n) -> anyhow::Result<()> {\n    let _span = tracy_client::span!(\"set_gamma_for_crtc\");\n\n    let info = device.get_crtc(crtc).context(\"error getting crtc info\")?;\n    let gamma_length = info.gamma_length() as usize;\n\n    ensure!(gamma_length != 0, \"setting gamma is not supported\");\n\n    let mut temp;\n    let ramp = if let Some(ramp) = ramp {\n        ensure!(ramp.len() == gamma_length * 3, \"wrong gamma length\");\n        ramp\n    } else {\n        let _span = tracy_client::span!(\"generate linear gamma\");\n\n        // The legacy API provides no way to reset the gamma, so set a linear one manually.\n        temp = vec![0u16; gamma_length * 3];\n\n        let (red, rest) = temp.split_at_mut(gamma_length);\n        let (green, blue) = rest.split_at_mut(gamma_length);\n        let denom = gamma_length as u64 - 1;\n        for (i, ((r, g), b)) in zip(zip(red, green), blue).enumerate() {\n            let value = (0xFFFFu64 * i as u64 / denom) as u16;\n            *r = value;\n            *g = value;\n            *b = value;\n        }\n\n        &temp\n    };\n\n    let (red, ramp) = ramp.split_at(gamma_length);\n    let (green, blue) = ramp.split_at(gamma_length);\n\n    device\n        .set_gamma(crtc, red, green, blue)\n        .context(\"error setting gamma\")?;\n\n    Ok(())\n}\n\nfn format_connector_name(connector: &connector::Info) -> String {\n    format!(\n        \"{}-{}\",\n        connector.interface().as_str(),\n        connector.interface_id(),\n    )\n}\n\nfn make_output_name(\n    device: &DrmDevice,\n    connector: connector::Handle,\n    connector_name: String,\n) -> OutputName {\n    let info = get_edid_info(device, connector)\n        .map_err(|err| warn!(\"error getting EDID info for {connector_name}: {err:?}\"))\n        .ok();\n    OutputName {\n        connector: connector_name,\n        make: info.as_ref().and_then(|info| info.make()),\n        model: info.as_ref().and_then(|info| info.model()),\n        serial: info.as_ref().and_then(|info| info.serial()),\n    }\n}\n\n/// Initializes the libinput plugin system.\n///\n/// # Safety\n///\n/// This function must be called before libinput iterates through the devices, i.e. before\n/// libinput_udev_assign_seat() or the first call to libinput_path_add_device().\nunsafe fn init_libinput_plugin_system(libinput: &Libinput) {\n    #[cfg(have_libinput_plugin_system)]\n    unsafe {\n        use std::ffi::{c_char, c_int, CString};\n        use std::os::unix::ffi::OsStringExt;\n\n        use directories::BaseDirs;\n        use input::ffi::libinput;\n        use input::AsRaw as _;\n\n        extern \"C\" {\n            fn libinput_plugin_system_append_path(libinput: *const libinput, path: *const c_char);\n            fn libinput_plugin_system_append_default_paths(libinput: *const libinput);\n            fn libinput_plugin_system_load_plugins(\n                libinput: *const libinput,\n                flags: c_int,\n            ) -> c_int;\n        }\n        const LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE: c_int = 0;\n        let libinput = libinput.as_raw();\n\n        // Also load plugins from $XDG_CONFIG_HOME/libinput/plugins.\n        if let Some(dirs) = BaseDirs::new() {\n            let mut plugins_dir = dirs.config_dir().to_path_buf();\n            plugins_dir.push(\"libinput\");\n            plugins_dir.push(\"plugins\");\n            if let Ok(plugins_dir) = CString::new(plugins_dir.into_os_string().into_vec()) {\n                libinput_plugin_system_append_path(libinput, plugins_dir.as_ptr());\n            }\n        }\n\n        libinput_plugin_system_append_default_paths(libinput);\n        libinput_plugin_system_load_plugins(libinput, LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE);\n    }\n    #[cfg(not(have_libinput_plugin_system))]\n    let _ = libinput;\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::assert_debug_snapshot;\n    use niri_config::output::Modeline;\n    use niri_ipc::{HSyncPolarity, VSyncPolarity};\n\n    use crate::backend::tty::{calculate_drm_mode_from_modeline, calculate_mode_cvt};\n\n    #[test]\n    fn test_calculate_drmmode_from_modeline() {\n        let modeline1 = Modeline {\n            clock: 173.0,\n            hdisplay: 1920,\n            vdisplay: 1080,\n            hsync_start: 2048,\n            hsync_end: 2248,\n            htotal: 2576,\n            vsync_start: 1083,\n            vsync_end: 1088,\n            vtotal: 1120,\n            hsync_polarity: HSyncPolarity::NHSync,\n            vsync_polarity: VSyncPolarity::PVSync,\n        };\n        assert_debug_snapshot!(calculate_drm_mode_from_modeline(&modeline1).unwrap(), @r#\"\n        Mode {\n            name: \"1920x1080@59.96\",\n            clock: 173000,\n            size: (\n                1920,\n                1080,\n            ),\n            hsync: (\n                2048,\n                2248,\n                2576,\n            ),\n            vsync: (\n                1083,\n                1088,\n                1120,\n            ),\n            hskew: 0,\n            vscan: 0,\n            vrefresh: 60,\n            mode_type: ModeTypeFlags(\n                USERDEF,\n            ),\n        }\n        \"#);\n        let modeline2 = Modeline {\n            clock: 452.5,\n            hdisplay: 1920,\n            vdisplay: 1080,\n            hsync_start: 2088,\n            hsync_end: 2296,\n            htotal: 2672,\n            vsync_start: 1083,\n            vsync_end: 1088,\n            vtotal: 1177,\n            hsync_polarity: HSyncPolarity::NHSync,\n            vsync_polarity: VSyncPolarity::PVSync,\n        };\n        assert_debug_snapshot!(calculate_drm_mode_from_modeline(&modeline2).unwrap(), @r#\"\n        Mode {\n            name: \"1920x1080@143.88\",\n            clock: 452500,\n            size: (\n                1920,\n                1080,\n            ),\n            hsync: (\n                2088,\n                2296,\n                2672,\n            ),\n            vsync: (\n                1083,\n                1088,\n                1177,\n            ),\n            hskew: 0,\n            vscan: 0,\n            vrefresh: 144,\n            mode_type: ModeTypeFlags(\n                USERDEF,\n            ),\n        }\n        \"#);\n    }\n\n    #[test]\n    fn test_calc_cvt() {\n        // Crosschecked with other calculators like the cvt commandline utility.\n        assert_debug_snapshot!(calculate_mode_cvt(1920, 1080, 60.0), @r#\"\n        Mode {\n            name: \"1920x1080@59.96\",\n            clock: 173000,\n            size: (\n                1920,\n                1080,\n            ),\n            hsync: (\n                2048,\n                2248,\n                2576,\n            ),\n            vsync: (\n                1083,\n                1088,\n                1120,\n            ),\n            hskew: 0,\n            vscan: 0,\n            vrefresh: 60,\n            mode_type: ModeTypeFlags(\n                USERDEF,\n            ),\n        }\n        \"#);\n        assert_debug_snapshot!(calculate_mode_cvt(1920, 1080, 144.0), @r#\"\n        Mode {\n            name: \"1920x1080@143.88\",\n            clock: 452500,\n            size: (\n                1920,\n                1080,\n            ),\n            hsync: (\n                2088,\n                2296,\n                2672,\n            ),\n            vsync: (\n                1083,\n                1088,\n                1177,\n            ),\n            hskew: 0,\n            vscan: 0,\n            vrefresh: 144,\n            mode_type: ModeTypeFlags(\n                USERDEF,\n            ),\n        }\n        \"#);\n    }\n}\n"
  },
  {
    "path": "src/backend/winit.rs",
    "content": "use std::cell::RefCell;\nuse std::collections::HashMap;\nuse std::mem;\nuse std::rc::Rc;\nuse std::sync::{Arc, Mutex};\n\nuse niri_config::{Config, OutputName};\nuse smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::renderer::damage::OutputDamageTracker;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::backend::renderer::{DebugFlags, ImportDma, ImportEgl, Renderer};\nuse smithay::backend::winit::{self, WinitEvent, WinitGraphicsBackend};\nuse smithay::output::{Mode, Output, PhysicalProperties, Subpixel};\nuse smithay::reexports::calloop::LoopHandle;\nuse smithay::reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback;\nuse smithay::reexports::winit::dpi::LogicalSize;\nuse smithay::reexports::winit::platform::wayland::WindowAttributesExtWayland;\nuse smithay::reexports::winit::window::Window;\nuse smithay::wayland::presentation::Refresh;\n\nuse super::{IpcOutputMap, OutputId, RenderResult};\nuse crate::niri::{Niri, RedrawState, State};\nuse crate::render_helpers::debug::draw_damage;\nuse crate::render_helpers::{resources, shaders, RenderTarget};\nuse crate::utils::{get_monotonic_time, logical_output};\n\npub struct Winit {\n    config: Rc<RefCell<Config>>,\n    output: Output,\n    backend: WinitGraphicsBackend<GlesRenderer>,\n    damage_tracker: OutputDamageTracker,\n    ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n}\n\nimpl Winit {\n    pub fn new(\n        config: Rc<RefCell<Config>>,\n        event_loop: LoopHandle<State>,\n    ) -> Result<Self, winit::Error> {\n        let _span = tracy_client::span!(\"Winit::new\");\n\n        let builder = Window::default_attributes()\n            .with_inner_size(LogicalSize::new(1280.0, 800.0))\n            // .with_resizable(false)\n            .with_title(\"niri\")\n            .with_name(\"niri\", \"\");\n        let (backend, winit) = winit::init_from_attributes(builder)?;\n\n        let output = Output::new(\n            \"winit\".to_string(),\n            PhysicalProperties {\n                size: (0, 0).into(),\n                subpixel: Subpixel::Unknown,\n                make: \"Smithay\".into(),\n                model: \"Winit\".into(),\n                serial_number: \"Unknown\".into(),\n            },\n        );\n\n        let mode = Mode {\n            size: backend.window_size(),\n            refresh: 60_000,\n        };\n        output.change_current_state(Some(mode), None, None, None);\n        output.set_preferred(mode);\n\n        output.user_data().insert_if_missing(|| OutputName {\n            connector: \"winit\".to_string(),\n            make: Some(\"Smithay\".to_string()),\n            model: Some(\"Winit\".to_string()),\n            serial: None,\n        });\n\n        let physical_properties = output.physical_properties();\n        let ipc_outputs = Arc::new(Mutex::new(HashMap::from([(\n            OutputId::next(),\n            niri_ipc::Output {\n                name: output.name(),\n                make: physical_properties.make,\n                model: physical_properties.model,\n                serial: None,\n                physical_size: None,\n                modes: vec![niri_ipc::Mode {\n                    width: backend.window_size().w.clamp(0, u16::MAX as i32) as u16,\n                    height: backend.window_size().h.clamp(0, u16::MAX as i32) as u16,\n                    refresh_rate: 60_000,\n                    is_preferred: true,\n                }],\n                current_mode: Some(0),\n                is_custom_mode: true,\n                vrr_supported: false,\n                vrr_enabled: false,\n                logical: Some(logical_output(&output)),\n            },\n        )])));\n\n        let damage_tracker = OutputDamageTracker::from_output(&output);\n\n        event_loop\n            .insert_source(winit, move |event, _, state| match event {\n                WinitEvent::Resized { size, .. } => {\n                    let winit = state.backend.winit();\n                    winit.output.change_current_state(\n                        Some(Mode {\n                            size,\n                            refresh: 60_000,\n                        }),\n                        None,\n                        None,\n                        None,\n                    );\n\n                    {\n                        let mut ipc_outputs = winit.ipc_outputs.lock().unwrap();\n                        let output = ipc_outputs.values_mut().next().unwrap();\n                        let mode = &mut output.modes[0];\n                        mode.width = size.w.clamp(0, u16::MAX as i32) as u16;\n                        mode.height = size.h.clamp(0, u16::MAX as i32) as u16;\n                        if let Some(logical) = output.logical.as_mut() {\n                            logical.width = size.w as u32;\n                            logical.height = size.h as u32;\n                        }\n                        state.niri.ipc_outputs_changed = true;\n                    }\n\n                    state.niri.output_resized(&winit.output);\n                }\n                WinitEvent::Input(event) => state.process_input_event(event),\n                WinitEvent::Focus(_) => (),\n                WinitEvent::Redraw => state.niri.queue_redraw(&state.backend.winit().output),\n                WinitEvent::CloseRequested => state.niri.stop_signal.stop(),\n            })\n            .unwrap();\n\n        Ok(Self {\n            config,\n            output,\n            backend,\n            damage_tracker,\n            ipc_outputs,\n        })\n    }\n\n    pub fn init(&mut self, niri: &mut Niri) {\n        let renderer = self.backend.renderer();\n        if let Err(err) = renderer.bind_wl_display(&niri.display_handle) {\n            warn!(\"error binding renderer wl_display: {err}\");\n        }\n\n        resources::init(renderer);\n        shaders::init(renderer);\n\n        let config = self.config.borrow();\n        if let Some(src) = config.animations.window_resize.custom_shader.as_deref() {\n            shaders::set_custom_resize_program(renderer, Some(src));\n        }\n        if let Some(src) = config.animations.window_close.custom_shader.as_deref() {\n            shaders::set_custom_close_program(renderer, Some(src));\n        }\n        if let Some(src) = config.animations.window_open.custom_shader.as_deref() {\n            shaders::set_custom_open_program(renderer, Some(src));\n        }\n        drop(config);\n\n        niri.update_shaders();\n\n        niri.add_output(self.output.clone(), None, false);\n    }\n\n    pub fn seat_name(&self) -> String {\n        \"winit\".to_owned()\n    }\n\n    pub fn with_primary_renderer<T>(\n        &mut self,\n        f: impl FnOnce(&mut GlesRenderer) -> T,\n    ) -> Option<T> {\n        Some(f(self.backend.renderer()))\n    }\n\n    pub fn render(&mut self, niri: &mut Niri, output: &Output) -> RenderResult {\n        let _span = tracy_client::span!(\"Winit::render\");\n\n        // Render the elements.\n        let mut elements = niri.render::<GlesRenderer>(\n            self.backend.renderer(),\n            output,\n            true,\n            RenderTarget::Output,\n        );\n\n        // Visualize the damage, if enabled.\n        if niri.debug_draw_damage {\n            let output_state = niri.output_state.get_mut(output).unwrap();\n            draw_damage(&mut output_state.debug_damage_tracker, &mut elements);\n        }\n\n        // Hand them over to winit.\n        let res = {\n            let (renderer, mut framebuffer) = self.backend.bind().unwrap();\n            // FIXME: currently impossible to call due to a mutable borrow.\n            //\n            // let age = self.backend.buffer_age().unwrap();\n            let age = 0;\n            self.damage_tracker\n                .render_output(renderer, &mut framebuffer, age, &elements, [0.; 4])\n                .unwrap()\n        };\n\n        niri.update_primary_scanout_output(output, &res.states);\n\n        let rv;\n        if let Some(damage) = res.damage {\n            if self\n                .config\n                .borrow()\n                .debug\n                .wait_for_frame_completion_before_queueing\n            {\n                let _span = tracy_client::span!(\"wait for completion\");\n                if let Err(err) = res.sync.wait() {\n                    warn!(\"error waiting for frame completion: {err:?}\");\n                }\n            }\n\n            self.backend.submit(Some(damage)).unwrap();\n\n            let mut presentation_feedbacks = niri.take_presentation_feedbacks(output, &res.states);\n            presentation_feedbacks.presented::<_, smithay::utils::Monotonic>(\n                get_monotonic_time(),\n                Refresh::Unknown,\n                0,\n                wp_presentation_feedback::Kind::empty(),\n            );\n\n            rv = RenderResult::Submitted;\n        } else {\n            rv = RenderResult::NoDamage;\n        }\n\n        let output_state = niri.output_state.get_mut(output).unwrap();\n        match mem::replace(&mut output_state.redraw_state, RedrawState::Idle) {\n            RedrawState::Idle => unreachable!(),\n            RedrawState::Queued => (),\n            RedrawState::WaitingForVBlank { .. } => unreachable!(),\n            RedrawState::WaitingForEstimatedVBlank(_) => unreachable!(),\n            RedrawState::WaitingForEstimatedVBlankAndQueued(_) => unreachable!(),\n        }\n\n        output_state.frame_callback_sequence = output_state.frame_callback_sequence.wrapping_add(1);\n\n        // FIXME: this should wait until a frame callback from the host compositor, but it redraws\n        // right away instead.\n        if output_state.unfinished_animations_remain {\n            self.backend.window().request_redraw();\n        }\n\n        rv\n    }\n\n    pub fn toggle_debug_tint(&mut self) {\n        let renderer = self.backend.renderer();\n        renderer.set_debug_flags(renderer.debug_flags() ^ DebugFlags::TINT);\n    }\n\n    pub fn import_dmabuf(&mut self, dmabuf: &Dmabuf) -> bool {\n        match self.backend.renderer().import_dmabuf(dmabuf, None) {\n            Ok(_texture) => true,\n            Err(err) => {\n                debug!(\"error importing dmabuf: {err:?}\");\n                false\n            }\n        }\n    }\n\n    pub fn ipc_outputs(&self) -> Arc<Mutex<IpcOutputMap>> {\n        self.ipc_outputs.clone()\n    }\n}\n"
  },
  {
    "path": "src/cli.rs",
    "content": "use std::ffi::OsString;\nuse std::path::PathBuf;\n\nuse clap::{Parser, Subcommand};\nuse clap_complete::Shell;\nuse niri_ipc::{Action, OutputAction};\n\nuse crate::utils::version;\n\n#[derive(Parser)]\n#[command(author, version = version(), about, long_about = None)]\n#[command(args_conflicts_with_subcommands = true)]\n#[command(subcommand_value_name = \"SUBCOMMAND\")]\n#[command(subcommand_help_heading = \"Subcommands\")]\npub struct Cli {\n    /// Path to config file (default: `$XDG_CONFIG_HOME/niri/config.kdl`).\n    ///\n    /// This can also be set with the `NIRI_CONFIG` environment variable. If both are set, the\n    /// command line argument takes precedence.\n    #[arg(short, long)]\n    pub config: Option<PathBuf>,\n    /// Import environment globally to systemd and D-Bus, run D-Bus services.\n    ///\n    /// Set this flag in a systemd service started by your display manager, or when running\n    /// manually as your main compositor instance. Do not set when running as a nested window, or\n    /// on a TTY as your non-main compositor instance, to avoid messing up the global environment.\n    #[arg(long)]\n    pub session: bool,\n    /// Command to run upon compositor startup.\n    #[arg(last = true)]\n    pub command: Vec<OsString>,\n\n    #[command(subcommand)]\n    pub subcommand: Option<Sub>,\n}\n\n#[derive(Subcommand)]\npub enum Sub {\n    /// Communicate with the running niri instance.\n    Msg {\n        #[command(subcommand)]\n        msg: Msg,\n        /// Format output as JSON.\n        #[arg(short, long)]\n        json: bool,\n    },\n    /// Validate the config file.\n    Validate {\n        /// Path to config file (default: `$XDG_CONFIG_HOME/niri/config.kdl`).\n        ///\n        /// This can also be set with the `NIRI_CONFIG` environment variable. If both are set, the\n        /// command line argument takes precedence.\n        #[arg(short, long)]\n        config: Option<PathBuf>,\n    },\n    /// Cause a panic to check if the backtraces are good.\n    Panic,\n    /// Generate shell completions.\n    Completions { shell: CompletionShell },\n}\n\n#[derive(Subcommand)]\npub enum Msg {\n    /// List connected outputs.\n    Outputs,\n    /// List workspaces.\n    Workspaces,\n    /// List open windows.\n    Windows,\n    /// List open layer-shell surfaces.\n    Layers,\n    /// Get the configured keyboard layouts.\n    KeyboardLayouts,\n    /// Print information about the focused output.\n    FocusedOutput,\n    /// Print information about the focused window.\n    FocusedWindow,\n    /// Pick a window with the mouse and print information about it.\n    PickWindow,\n    /// Pick a color from the screen with the mouse.\n    PickColor,\n    /// Perform an action.\n    Action {\n        #[command(subcommand)]\n        action: Action,\n    },\n    /// Change output configuration temporarily.\n    ///\n    /// The configuration is changed temporarily and not saved into the config file. If the output\n    /// configuration subsequently changes in the config file, these temporary changes will be\n    /// forgotten.\n    Output {\n        /// Output name.\n        ///\n        /// Run `niri msg outputs` to see the output names.\n        #[arg()]\n        output: String,\n        /// Configuration to apply.\n        #[command(subcommand)]\n        action: OutputAction,\n    },\n    /// Start continuously receiving events from the compositor.\n    EventStream,\n    /// Print the version of the running niri instance.\n    Version,\n    /// Request an error from the running niri instance.\n    RequestError,\n    /// Print the overview state.\n    OverviewState,\n    /// List screencasts.\n    Casts,\n}\n\n#[derive(Clone, Debug, clap::ValueEnum)]\npub enum CompletionShell {\n    Bash,\n    Elvish,\n    Fish,\n    PowerShell,\n    Zsh,\n    Nushell,\n}\n\nimpl TryFrom<CompletionShell> for Shell {\n    type Error = &'static str;\n\n    fn try_from(shell: CompletionShell) -> Result<Self, Self::Error> {\n        match shell {\n            CompletionShell::Bash => Ok(Shell::Bash),\n            CompletionShell::Elvish => Ok(Shell::Elvish),\n            CompletionShell::Fish => Ok(Shell::Fish),\n            CompletionShell::PowerShell => Ok(Shell::PowerShell),\n            CompletionShell::Zsh => Ok(Shell::Zsh),\n            CompletionShell::Nushell => Err(\"Nushell should be handled separately\"),\n        }\n    }\n}\n"
  },
  {
    "path": "src/cursor.rs",
    "content": "use std::cell::RefCell;\nuse std::collections::HashMap;\nuse std::env;\nuse std::fs::File;\nuse std::io::Read;\nuse std::rc::Rc;\n\nuse anyhow::{anyhow, Context};\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::renderer::element::memory::MemoryRenderBuffer;\nuse smithay::input::pointer::{CursorIcon, CursorImageStatus, CursorImageSurfaceData};\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::utils::{IsAlive, Logical, Physical, Point, Transform};\nuse smithay::wayland::compositor::with_states;\nuse xcursor::parser::{parse_xcursor, Image};\nuse xcursor::CursorTheme;\n\n/// Some default looking `left_ptr` icon.\nstatic FALLBACK_CURSOR_DATA: &[u8] = include_bytes!(\"../resources/cursor.rgba\");\n\ntype XCursorCache = HashMap<(CursorIcon, i32), Option<Rc<XCursor>>>;\n\npub struct CursorManager {\n    theme: CursorTheme,\n    size: u8,\n    current_cursor: CursorImageStatus,\n    named_cursor_cache: RefCell<XCursorCache>,\n}\n\nimpl CursorManager {\n    pub fn new(theme: &str, size: u8) -> Self {\n        Self::ensure_env(theme, size);\n\n        let theme = CursorTheme::load(theme);\n\n        Self {\n            theme,\n            size,\n            current_cursor: CursorImageStatus::default_named(),\n            named_cursor_cache: Default::default(),\n        }\n    }\n\n    /// Reload the cursor theme.\n    pub fn reload(&mut self, theme: &str, size: u8) {\n        Self::ensure_env(theme, size);\n        self.theme = CursorTheme::load(theme);\n        self.size = size;\n        self.named_cursor_cache.get_mut().clear();\n    }\n\n    /// Checks if the cursor WlSurface is alive, and if not, cleans it up.\n    pub fn check_cursor_image_surface_alive(&mut self) {\n        if let CursorImageStatus::Surface(surface) = &self.current_cursor {\n            if !surface.alive() {\n                self.current_cursor = CursorImageStatus::default_named();\n            }\n        }\n    }\n\n    /// Get the current rendering cursor.\n    pub fn get_render_cursor(&self, scale: i32) -> RenderCursor {\n        match self.current_cursor.clone() {\n            CursorImageStatus::Hidden => RenderCursor::Hidden,\n            CursorImageStatus::Surface(surface) => {\n                let hotspot = with_states(&surface, |states| {\n                    states\n                        .data_map\n                        .get::<CursorImageSurfaceData>()\n                        .unwrap()\n                        .lock()\n                        .unwrap()\n                        .hotspot\n                });\n\n                RenderCursor::Surface { hotspot, surface }\n            }\n            CursorImageStatus::Named(icon) => self.get_render_cursor_named(icon, scale),\n        }\n    }\n\n    fn get_render_cursor_named(&self, icon: CursorIcon, scale: i32) -> RenderCursor {\n        self.get_cursor_with_name(icon, scale)\n            .map(|cursor| RenderCursor::Named {\n                icon,\n                scale,\n                cursor,\n            })\n            .unwrap_or_else(|| RenderCursor::Named {\n                icon: Default::default(),\n                scale,\n                cursor: self.get_default_cursor(scale),\n            })\n    }\n\n    pub fn is_current_cursor_animated(&self, scale: i32) -> bool {\n        match &self.current_cursor {\n            CursorImageStatus::Hidden => false,\n            CursorImageStatus::Surface(_) => false,\n            CursorImageStatus::Named(icon) => self\n                .get_cursor_with_name(*icon, scale)\n                .unwrap_or_else(|| self.get_default_cursor(scale))\n                .is_animated_cursor(),\n        }\n    }\n\n    /// Get named cursor for the given `icon` and `scale`.\n    pub fn get_cursor_with_name(&self, icon: CursorIcon, scale: i32) -> Option<Rc<XCursor>> {\n        self.named_cursor_cache\n            .borrow_mut()\n            .entry((icon, scale))\n            .or_insert_with_key(|(icon, scale)| {\n                let size = self.size as i32 * scale;\n                let mut cursor = Self::load_xcursor(&self.theme, icon.name(), size);\n\n                // Check alternative names to account for non-compliant themes.\n                if cursor.is_err() {\n                    for name in icon.alt_names() {\n                        cursor = Self::load_xcursor(&self.theme, name, size);\n                        if cursor.is_ok() {\n                            break;\n                        }\n                    }\n                }\n\n                if let Err(err) = &cursor {\n                    warn!(\"error loading xcursor {}@{size}: {err:?}\", icon.name());\n                }\n\n                // The default cursor must always have a fallback.\n                if *icon == CursorIcon::Default && cursor.is_err() {\n                    cursor = Ok(Self::fallback_cursor());\n                }\n\n                cursor.ok().map(Rc::new)\n            })\n            .clone()\n    }\n\n    /// Get default cursor.\n    pub fn get_default_cursor(&self, scale: i32) -> Rc<XCursor> {\n        // The default cursor always has a fallback.\n        self.get_cursor_with_name(CursorIcon::Default, scale)\n            .unwrap()\n    }\n\n    /// Currently used cursor_image as a cursor provider.\n    pub fn cursor_image(&self) -> &CursorImageStatus {\n        &self.current_cursor\n    }\n\n    /// Set new cursor image provider.\n    pub fn set_cursor_image(&mut self, cursor: CursorImageStatus) {\n        self.current_cursor = cursor;\n    }\n\n    /// Load the cursor with the given `name` from the file system picking the closest\n    /// one to the given `size`.\n    fn load_xcursor(theme: &CursorTheme, name: &str, size: i32) -> anyhow::Result<XCursor> {\n        let _span = tracy_client::span!(\"load_xcursor\");\n\n        let path = theme\n            .load_icon(name)\n            .ok_or_else(|| anyhow!(\"no default icon\"))?;\n\n        let mut file = File::open(path).context(\"error opening cursor icon file\")?;\n        let mut buf = vec![];\n        file.read_to_end(&mut buf)\n            .context(\"error reading cursor icon file\")?;\n\n        let mut images = parse_xcursor(&buf).context(\"error parsing cursor icon file\")?;\n\n        let (width, height) = images\n            .iter()\n            .min_by_key(|image| (size - image.size as i32).abs())\n            .map(|image| (image.width, image.height))\n            .unwrap();\n\n        images.retain(move |image| image.width == width && image.height == height);\n\n        let animation_duration = images.iter().fold(0, |acc, image| acc + image.delay);\n\n        Ok(XCursor {\n            images,\n            animation_duration,\n        })\n    }\n\n    /// Set the common XCURSOR env variables.\n    fn ensure_env(theme: &str, size: u8) {\n        env::set_var(\"XCURSOR_THEME\", theme);\n        env::set_var(\"XCURSOR_SIZE\", size.to_string());\n    }\n\n    fn fallback_cursor() -> XCursor {\n        let images = vec![Image {\n            size: 32,\n            width: 64,\n            height: 64,\n            xhot: 1,\n            yhot: 1,\n            delay: 0,\n            pixels_rgba: Vec::from(FALLBACK_CURSOR_DATA),\n            pixels_argb: vec![],\n        }];\n\n        XCursor {\n            images,\n            animation_duration: 0,\n        }\n    }\n}\n\n/// The cursor prepared for renderer.\npub enum RenderCursor {\n    Hidden,\n    Surface {\n        hotspot: Point<i32, Logical>,\n        surface: WlSurface,\n    },\n    Named {\n        icon: CursorIcon,\n        scale: i32,\n        cursor: Rc<XCursor>,\n    },\n}\n\ntype TextureCache = HashMap<(CursorIcon, i32), Vec<MemoryRenderBuffer>>;\n\n#[derive(Default)]\npub struct CursorTextureCache {\n    cache: RefCell<TextureCache>,\n}\n\nimpl CursorTextureCache {\n    pub fn clear(&mut self) {\n        self.cache.get_mut().clear();\n    }\n\n    pub fn get(\n        &self,\n        icon: CursorIcon,\n        scale: i32,\n        cursor: &XCursor,\n        idx: usize,\n    ) -> MemoryRenderBuffer {\n        self.cache\n            .borrow_mut()\n            .entry((icon, scale))\n            .or_insert_with(|| {\n                cursor\n                    .frames()\n                    .iter()\n                    .map(|frame| {\n                        MemoryRenderBuffer::from_slice(\n                            &frame.pixels_rgba,\n                            Fourcc::Argb8888,\n                            (frame.width as i32, frame.height as i32),\n                            scale,\n                            Transform::Normal,\n                            None,\n                        )\n                    })\n                    .collect()\n            })[idx]\n            .clone()\n    }\n}\n\n// The XCursorBuffer implementation is inspired by `wayland-rs`, thus provided under MIT license.\n\n/// The state of the `NamedCursor`.\npub struct XCursor {\n    /// The image for the underlying named cursor.\n    images: Vec<Image>,\n    /// The total duration of the animation.\n    animation_duration: u32,\n}\n\nimpl XCursor {\n    /// Given a time, calculate which frame to show, and how much time remains until the next frame.\n    ///\n    /// Time will wrap, so if for instance the cursor has an animation lasting 100ms,\n    /// then calling this function with 5ms and 105ms as input gives the same output.\n    pub fn frame(&self, mut millis: u32) -> (usize, &Image) {\n        if self.animation_duration == 0 {\n            return (0, &self.images[0]);\n        }\n\n        millis %= self.animation_duration;\n\n        let mut res = 0;\n        for (i, img) in self.images.iter().enumerate() {\n            if millis < img.delay {\n                res = i;\n                break;\n            }\n            millis -= img.delay;\n        }\n\n        (res, &self.images[res])\n    }\n\n    /// Get the frames for the given `XCursor`.\n    pub fn frames(&self) -> &[Image] {\n        &self.images\n    }\n\n    /// Check whether the cursor is animated.\n    pub fn is_animated_cursor(&self) -> bool {\n        self.images.len() > 1\n    }\n\n    /// Get hotspot for the given `image`.\n    pub fn hotspot(image: &Image) -> Point<i32, Physical> {\n        (image.xhot as i32, image.yhot as i32).into()\n    }\n}\n"
  },
  {
    "path": "src/dbus/freedesktop_a11y.rs",
    "content": "// References:\n// - https://invent.kde.org/plasma/kwin/-/blob/397fbbe52a8f2d855ad0c9817b51a9bdf06a68e2/src/a11ykeyboardmonitor.cpp#L41\n// - https://gitlab.gnome.org/GNOME/mutter/-/blob/cbb7295ac1f93a2dfd55a7c0544688e7e5c4d2e2/src/backends/meta-a11y-manager.c\n\nuse std::collections::{HashMap, HashSet};\nuse std::sync::{Arc, Mutex, OnceLock};\nuse std::time::Duration;\n\nuse anyhow::Context;\nuse futures_util::StreamExt;\nuse smithay::backend::input::{KeyState, Keycode};\nuse smithay::input::keyboard::{xkb, Keysym};\nuse zbus::blocking::object_server::InterfaceRef;\nuse zbus::fdo::{self, RequestNameFlags};\nuse zbus::interface;\nuse zbus::message::Header;\nuse zbus::names::{BusName, OwnedUniqueName, UniqueName};\nuse zbus::object_server::SignalEmitter;\nuse zbus::zvariant::NoneValue;\n\nuse super::Start;\nuse crate::niri::State;\n\n#[derive(Debug, Default)]\nstruct Data {\n    clients: HashMap<OwnedUniqueName, Client>,\n\n    grabbed_mods: HashSet<Keysym>,\n    grabbed_mod_last_press_time: HashMap<Keysym, Duration>,\n    suppressed_keys: HashSet<Keysym>,\n}\n\n#[derive(Debug, Default)]\nstruct Client {\n    watched: bool,\n    grabbed: bool,\n    modifiers: HashSet<Keysym>,\n    keystrokes: Vec<(Keysym, u32)>,\n}\n\n#[derive(Clone)]\npub struct KeyboardMonitor {\n    data: Arc<Mutex<Data>>,\n    iface: Arc<OnceLock<InterfaceRef<Self>>>,\n}\n\n/// Keyboard monitor key block reason.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum KbMonBlock {\n    /// Not blocked.\n    Pass,\n    /// Blocked, and this is the first press/release of the a11y modifier.\n    ModifierFirstPress,\n    /// Blocked, and this is not the a11y modifier.\n    Block,\n}\n\n/// Interface for monitoring of keyboard input by assistive technologies.\n///\n/// This interface is used by assistive technologies to monitor keyboard input of the compositor.\n/// The compositor is expected to listen on the well-known bus name \"org.freedesktop.a11y.Manager\"\n/// at the object path \"/org/freedesktop/a11y/Manager\".\n#[interface(name = \"org.freedesktop.a11y.KeyboardMonitor\")]\nimpl KeyboardMonitor {\n    // Starts grabbing all key events. The client receives the events through the KeyEvent signal,\n    // and in addition, the events aren't handled normally by the compositor. This includes changes\n    // to the state of toggles like Caps Lock, Num Lock, and Scroll Lock.\n    //\n    // This behavior stays in effect until the same client calls UngrabKeyboard or closes its D-Bus\n    // connection.\n    async fn grab_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {\n        let Some(sender) = hdr.sender() else {\n            return Err(fdo::Error::Failed(\"no sender\".to_owned()));\n        };\n        let sender = OwnedUniqueName::from(sender.to_owned());\n        trace!(\"enabling keyboard grab for {sender}\");\n\n        let mut data = self.data.lock().unwrap();\n        let client = data.clients.entry(sender).or_default();\n        client.grabbed = true;\n\n        Ok(())\n    }\n\n    // Reverses the effect of calling GrabKeyboard. If GrabKeyboard wasn't previously called, this\n    // method does nothing.\n    //\n    // After calling this method, the key grabs specified in the last call to SetKeyGrabs, if any,\n    // are still in effect. Also, the client will still receive key events through the KeyEvent\n    // signal, if it has called WatchKeyboard.\n    async fn ungrab_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {\n        let Some(sender) = hdr.sender() else {\n            return Err(fdo::Error::Failed(\"no sender\".to_owned()));\n        };\n        let sender = OwnedUniqueName::from(sender.to_owned());\n\n        let mut data = self.data.lock().unwrap();\n        if let Some(client) = data.clients.get_mut(&sender) {\n            trace!(\"disabling keyboard grab for {sender}\");\n            client.grabbed = false;\n        }\n\n        Ok(())\n    }\n\n    // Starts watching all key events. The client receives the events through the KeyEvent signal,\n    // but the events are still handled normally by the compositor. This includes changes to the\n    // state of toggles like Caps Lock, Num Lock, and Scroll Lock.\n    //\n    // This behavior stays in effect until the same client calls UnwatchKeyboard or closes its D-Bus\n    // connection.\n    async fn watch_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {\n        let Some(sender) = hdr.sender() else {\n            return Err(fdo::Error::Failed(\"no sender\".to_owned()));\n        };\n        let sender = OwnedUniqueName::from(sender.to_owned());\n        trace!(\"enabling keyboard watch for {sender}\");\n\n        let mut data = self.data.lock().unwrap();\n        let client = data.clients.entry(sender).or_default();\n        client.watched = true;\n\n        Ok(())\n    }\n\n    // Reverses the effect of calling WatchKeyboard. If WatchKeyboard wasn't previously called, this\n    // method does nothing.\n    //\n    // After calling this method, the key grabs specified in the last call to SetKeyGrabs, if any,\n    // are still in effect, but other key events are no longer reported to this client.\n    async fn unwatch_keyboard(&self, #[zbus(header)] hdr: Header<'_>) -> fdo::Result<()> {\n        let Some(sender) = hdr.sender() else {\n            return Err(fdo::Error::Failed(\"no sender\".to_owned()));\n        };\n        let sender = OwnedUniqueName::from(sender.to_owned());\n\n        let mut data = self.data.lock().unwrap();\n        if let Some(client) = data.clients.get_mut(&sender) {\n            trace!(\"disabling keyboard watch for {sender}\");\n            client.watched = false;\n        }\n\n        Ok(())\n    }\n\n    // Sets the current key grabs for the calling client, overriding any previous call to this\n    // method. For grabbed key events, the KeyEvent signal is emitted, and normal key event handling\n    // is suppressed, including state changes for toggles like Caps Lock and Num Lock.\n    //\n    // The grabs set by this method stay in effect until the same client calls this method again, or\n    // until that client closes its D-Bus connection.\n    //\n    // Each item in `modifiers` is an XKB keysym. All keys in this list will be grabbed, and keys\n    // pressed while any of these keys are down will also be grabbed.\n    //\n    // Each item in `keystrokes` is a struct with the following fields:\n    //\n    // - the XKB keysym of the non-modifier key\n    // - the XKB modifier mask of the modifiers, if any, for this keystroke\n    //\n    // If any of the keys in `modifiers` is pressed alone, the compositor is required to ignore the\n    // key press and release event if a second key press of the same modifier is not received within\n    // a reasonable time frame, for example, the key repeat delay. If such event is received, this\n    // second event is processed normally.\n    async fn set_key_grabs(\n        &self,\n        #[zbus(header)] hdr: Header<'_>,\n        modifiers: Vec<u32>,\n        keystrokes: Vec<(u32, u32)>,\n    ) -> fdo::Result<()> {\n        let Some(sender) = hdr.sender() else {\n            return Err(fdo::Error::Failed(\"no sender\".to_owned()));\n        };\n        let sender = OwnedUniqueName::from(sender.to_owned());\n        trace!(\"updating key grabs for {sender}\");\n\n        let mut data = self.data.lock().unwrap();\n        let client = data.clients.entry(sender).or_default();\n        client.modifiers = HashSet::from_iter(modifiers.into_iter().map(Keysym::new));\n        client.keystrokes =\n            Vec::from_iter(keystrokes.into_iter().map(|(k, v)| (Keysym::new(k), v)));\n\n        data.rebuild_grabbed_mods();\n\n        Ok(())\n    }\n\n    // The compositor emits this signal for each key press or release.\n    //\n    // - `released`: whether this is a key-up event\n    // - `state`: XKB modifier mask for currently pressed modifiers\n    // - `keysym`: XKB keysym for this key\n    // - `unichar`: Unicode character for this key, or 0 if none\n    // - `keycode`: hardware-dependent keycode for this key\n    #[zbus(signal)]\n    pub async fn key_event(\n        ctxt: &SignalEmitter<'_>,\n        released: bool,\n        state: u32,\n        keysym: u32,\n        unichar: u32,\n        keycode: u16,\n    ) -> zbus::Result<()>;\n}\n\nimpl KeyboardMonitor {\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        Self {\n            data: Arc::new(Mutex::new(Data::default())),\n            iface: Arc::new(OnceLock::new()),\n        }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn process_key(\n        &self,\n        repeat_delay: Duration,\n        time: Duration,\n        keycode: Keycode,\n        released: bool,\n        mods: u32,\n        keysym: Keysym,\n        unichar: u32,\n    ) -> KbMonBlock {\n        let _span = tracy_client::span!(\"KeyboardMonitor::process_key\");\n\n        let mut ctxt = self.iface.get().unwrap().signal_emitter().clone();\n\n        let mut data = self.data.lock().unwrap();\n\n        // Emit key events as necessary.\n        for (name, client) in &data.clients {\n            if client.should_watch_keypress(&data.suppressed_keys, mods, keysym) {\n                let _span = tracy_client::span!(\"emitting key event\");\n\n                // Emit to that client only.\n                ctxt = ctxt.set_destination(BusName::Unique(name.as_ref()));\n                let ctxt = &ctxt;\n                async_io::block_on(async move {\n                    if let Err(err) = KeyboardMonitor::key_event(\n                        ctxt,\n                        released,\n                        mods,\n                        keysym.raw(),\n                        unichar,\n                        keycode.raw() as u16,\n                    )\n                    .await\n                    {\n                        warn!(\"error emitting key_event: {err:?}\");\n                    }\n                });\n            }\n        }\n\n        // Check for double-pressed grabbed modifier that should not be captured.\n        if data.grabbed_mods.contains(&keysym) {\n            if released {\n                // If missing from suppressed keys, then this is a release corresponding to the\n                // second press that got handled normally.\n                if !data.suppressed_keys.contains(&keysym) {\n                    trace!(\"handling release for second press of grabbed modifier: {keysym:?}\");\n                    return KbMonBlock::Pass;\n                }\n            } else {\n                let last_press_entry = data\n                    .grabbed_mod_last_press_time\n                    .entry(keysym)\n                    .or_insert(Duration::ZERO);\n                let last_press = *last_press_entry;\n                *last_press_entry = time;\n\n                // Modifier pressed twice; handle it as normal.\n                if time <= last_press.saturating_add(repeat_delay) {\n                    trace!(\"handling second press of grabbed modifier: {keysym:?}\");\n                    return KbMonBlock::Pass;\n                }\n            }\n        }\n\n        let mut block = false;\n\n        if released {\n            // This is a release for a key that was grabbed.\n            if data.suppressed_keys.remove(&keysym) {\n                trace!(\"blocking release for previously suppressed key: {keysym:?}\");\n                block = true;\n            }\n        } else if data.suppressed_keys.contains(&keysym) {\n            // Second press for an already-pressed key, e.g. from two keyboards.\n            trace!(\"blocking press for already-pressed key: {keysym:?}\");\n            block = true;\n        } else {\n            // Check if it's grabbed by any client.\n            if data\n                .clients\n                .values()\n                .any(|client| client.should_grab_keypress(&data.suppressed_keys, mods, keysym))\n            {\n                trace!(\"blocking press for grabbed key: {keysym:?}\");\n                data.suppressed_keys.insert(keysym);\n                block = true;\n            }\n        }\n\n        if !block {\n            KbMonBlock::Pass\n        } else if data.grabbed_mods.contains(&keysym) {\n            KbMonBlock::ModifierFirstPress\n        } else {\n            KbMonBlock::Block\n        }\n    }\n}\n\nimpl Data {\n    fn rebuild_grabbed_mods(&mut self) {\n        self.grabbed_mods.clear();\n        for client in self.clients.values() {\n            self.grabbed_mods.extend(&client.modifiers);\n        }\n    }\n}\n\nimpl Client {\n    fn should_grab_keypress(\n        &self,\n        suppressed_keys: &HashSet<Keysym>,\n        mods: u32,\n        keysym: Keysym,\n    ) -> bool {\n        // Grabbing all keys.\n        if self.grabbed {\n            return true;\n        }\n\n        for modifier in &self.modifiers {\n            // This is a grabbed modifier, or a grabbed modifier is currently down.\n            if *modifier == keysym || suppressed_keys.contains(modifier) {\n                return true;\n            }\n        }\n\n        for (grabbed_keysym, grabbed_mods) in &self.keystrokes {\n            // This is a grabbed keystroke.\n            if *grabbed_keysym == keysym && *grabbed_mods == mods {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    fn should_watch_keypress(\n        &self,\n        suppressed_keys: &HashSet<Keysym>,\n        mods: u32,\n        keysym: Keysym,\n    ) -> bool {\n        if self.watched {\n            return true;\n        }\n\n        self.should_grab_keypress(suppressed_keys, mods, keysym)\n    }\n}\n\nasync fn monitor_disappeared_clients(\n    conn: &zbus::Connection,\n    data: Arc<Mutex<Data>>,\n) -> anyhow::Result<()> {\n    let proxy = fdo::DBusProxy::new(conn)\n        .await\n        .context(\"error creating a DBusProxy\")?;\n\n    let mut stream = proxy\n        .receive_name_owner_changed_with_args(&[(2, UniqueName::null_value())])\n        .await\n        .context(\"error creating a NameOwnerChanged stream\")?;\n\n    while let Some(signal) = stream.next().await {\n        let args = signal\n            .args()\n            .context(\"error retrieving NameOwnerChanged args\")?;\n\n        let Some(name) = &**args.old_owner() else {\n            continue;\n        };\n\n        if args.new_owner().is_none() {\n            trace!(\"keyboard monitor client disconnected: {name}\");\n\n            let name = OwnedUniqueName::from(name.to_owned());\n            let mut data = data.lock().unwrap();\n            data.clients.remove(&name);\n            data.rebuild_grabbed_mods();\n        } else {\n            error!(\"non-null new_owner should've been filtered out\");\n        }\n    }\n\n    Ok(())\n}\n\nimpl Start for KeyboardMonitor {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {\n        let data = self.data.clone();\n\n        let conn = zbus::blocking::Connection::session()?;\n        let flags = RequestNameFlags::AllowReplacement\n            | RequestNameFlags::ReplaceExisting\n            | RequestNameFlags::DoNotQueue;\n\n        conn.object_server()\n            .at(\"/org/freedesktop/a11y/Manager\", self.clone())?;\n        conn.request_name_with_flags(\"org.freedesktop.a11y.Manager\", flags)?;\n\n        let iface = conn\n            .object_server()\n            .interface(\"/org/freedesktop/a11y/Manager\")?;\n        let _ = self.iface.set(iface);\n\n        let async_conn = conn.inner().clone();\n        let future = async move {\n            if let Err(err) = monitor_disappeared_clients(&async_conn, data.clone()).await {\n                warn!(\"error monitoring keyboard monitor clients: {err:?}\");\n\n                // Since the monitor is now broken, prevent any further communication.\n                if let Err(err) = async_conn.close().await {\n                    warn!(\"error closing connection: {err:?}\");\n                }\n\n                let mut data = data.lock().unwrap();\n                data.clients.clear();\n                data.rebuild_grabbed_mods();\n            }\n        };\n        let task = conn\n            .inner()\n            .executor()\n            .spawn(future, \"monitor disappearing keyboard clients\");\n        task.detach();\n\n        Ok(conn)\n    }\n}\n\nimpl State {\n    pub fn a11y_process_key(\n        &mut self,\n        time: Duration,\n        keycode: Keycode,\n        state: KeyState,\n    ) -> KbMonBlock {\n        if self.niri.a11y_keyboard_monitor.is_none() {\n            return KbMonBlock::Pass;\n        }\n\n        let keyboard = self.niri.seat.get_keyboard().unwrap();\n\n        let (mods, keysym, unichar) = keyboard.with_xkb_state(self, |context| {\n            let xkb = context.xkb().lock().unwrap();\n            // SAFETY: we're not changing the ref count.\n            let state = unsafe { xkb.state() };\n\n            let keysym = state.key_get_one_sym(keycode);\n            let mods = state.serialize_mods(xkb::STATE_MODS_EFFECTIVE);\n            let unichar = state.key_get_utf32(keycode);\n\n            (mods, keysym, unichar)\n        });\n\n        let config = self.niri.config.borrow();\n        let repeat_delay = Duration::from_millis(u64::from(config.input.keyboard.repeat_delay));\n        let released = state == KeyState::Released;\n\n        let Some(monitor) = &self.niri.a11y_keyboard_monitor else {\n            return KbMonBlock::Pass;\n        };\n        monitor.process_key(repeat_delay, time, keycode, released, mods, keysym, unichar)\n    }\n}\n"
  },
  {
    "path": "src/dbus/freedesktop_locale1.rs",
    "content": "use futures_util::StreamExt;\nuse niri_config::Xkb;\nuse zbus::names::InterfaceName;\nuse zbus::{fdo, zvariant};\n\npub enum Locale1ToNiri {\n    XkbChanged(Xkb),\n}\n\npub fn start(\n    to_niri: calloop::channel::Sender<Locale1ToNiri>,\n) -> anyhow::Result<zbus::blocking::Connection> {\n    let conn = zbus::blocking::Connection::system()?;\n\n    let async_conn = conn.inner().clone();\n    let future = async move {\n        let proxy = fdo::PropertiesProxy::new(\n            &async_conn,\n            \"org.freedesktop.locale1\",\n            \"/org/freedesktop/locale1\",\n        )\n        .await;\n        let proxy = match proxy {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error creating PropertiesProxy: {err:?}\");\n                return;\n            }\n        };\n\n        let mut props_changed = match proxy.receive_properties_changed().await {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error subscribing to PropertiesChanged: {err:?}\");\n                return;\n            }\n        };\n\n        let props = proxy\n            .get_all(InterfaceName::try_from(\"org.freedesktop.locale1\").unwrap())\n            .await;\n        let mut props = match props {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error receiving initial properties: {err:?}\");\n                return;\n            }\n        };\n\n        trace!(\"initial properties: {props:?}\");\n\n        let mut get = |name| {\n            props\n                .remove(name)\n                .and_then(|x| String::try_from(x).ok())\n                .unwrap_or_default()\n        };\n\n        let mut xkb = Xkb {\n            rules: String::new(),\n            model: get(\"X11Model\"),\n            layout: get(\"X11Layout\"),\n            variant: get(\"X11Variant\"),\n            options: match get(\"X11Options\") {\n                x if x.is_empty() => None,\n                x => Some(x),\n            },\n            file: None,\n        };\n\n        // Send the initial properties.\n        if let Err(err) = to_niri.send(Locale1ToNiri::XkbChanged(xkb.clone())) {\n            warn!(\"error sending message to niri: {err:?}\");\n            return;\n        };\n\n        while let Some(changed) = props_changed.next().await {\n            let args = match changed.args() {\n                Ok(args) => args,\n                Err(err) => {\n                    warn!(\"error parsing locale1 PropertiesChanged args: {err:?}\");\n                    return;\n                }\n            };\n\n            let mut changed = false;\n            for (name, value) in args.changed_properties() {\n                trace!(\"changed property: {name} => {value:?}\");\n\n                let value = zvariant::Str::try_from(value).unwrap_or_default();\n                let value = value.as_str();\n\n                match *name {\n                    \"X11Model\" => {\n                        if xkb.model != value {\n                            xkb.model = String::from(value);\n                            changed = true;\n                        }\n                    }\n                    \"X11Layout\" => {\n                        if xkb.layout != value {\n                            xkb.layout = String::from(value);\n                            changed = true;\n                        }\n                    }\n                    \"X11Variant\" => {\n                        if xkb.variant != value {\n                            xkb.variant = String::from(value);\n                            changed = true;\n                        }\n                    }\n                    \"X11Options\" => {\n                        let value = match value {\n                            \"\" => None,\n                            x => Some(x),\n                        };\n                        if xkb.options.as_deref() != value {\n                            xkb.options = value.map(String::from);\n                            changed = true;\n                        }\n                    }\n                    _ => (),\n                }\n            }\n\n            if !changed {\n                continue;\n            }\n\n            if let Err(err) = to_niri.send(Locale1ToNiri::XkbChanged(xkb.clone())) {\n                warn!(\"error sending message to niri: {err:?}\");\n                return;\n            };\n        }\n    };\n\n    let task = conn\n        .inner()\n        .executor()\n        .spawn(future, \"monitor locale1 property changes\");\n    task.detach();\n\n    Ok(conn)\n}\n"
  },
  {
    "path": "src/dbus/freedesktop_login1.rs",
    "content": "use futures_util::StreamExt;\nuse zbus::fdo;\nuse zbus::names::InterfaceName;\n\npub enum Login1ToNiri {\n    LidClosedChanged(bool),\n}\n\npub fn start(\n    to_niri: calloop::channel::Sender<Login1ToNiri>,\n) -> anyhow::Result<zbus::blocking::Connection> {\n    let conn = zbus::blocking::Connection::system()?;\n\n    let async_conn = conn.inner().clone();\n    let future = async move {\n        let proxy = fdo::PropertiesProxy::new(\n            &async_conn,\n            \"org.freedesktop.login1\",\n            \"/org/freedesktop/login1\",\n        )\n        .await;\n        let proxy = match proxy {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error creating PropertiesProxy: {err:?}\");\n                return;\n            }\n        };\n\n        let mut props_changed = match proxy.receive_properties_changed().await {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error subscribing to PropertiesChanged: {err:?}\");\n                return;\n            }\n        };\n\n        let props = proxy\n            .get_all(InterfaceName::try_from(\"org.freedesktop.login1.Manager\").unwrap())\n            .await;\n        let mut props = match props {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error receiving initial properties: {err:?}\");\n                return;\n            }\n        };\n\n        trace!(\"initial properties: {props:?}\");\n\n        let mut lid_closed = props\n            .remove(\"LidClosed\")\n            .and_then(|value| bool::try_from(value).ok())\n            .unwrap_or_default();\n\n        if let Err(err) = to_niri.send(Login1ToNiri::LidClosedChanged(lid_closed)) {\n            warn!(\"error sending initial lid state to niri: {err:?}\");\n            return;\n        };\n\n        while let Some(signal) = props_changed.next().await {\n            let args = match signal.args() {\n                Ok(args) => args,\n                Err(err) => {\n                    warn!(\"error parsing PropertiesChanged args: {err:?}\");\n                    return;\n                }\n            };\n\n            let mut new_lid_closed = lid_closed;\n            let mut changed = false;\n            for (name, value) in args.changed_properties() {\n                trace!(\"changed property: {name} => {value:?}\");\n                if *name != \"LidClosed\" {\n                    continue;\n                }\n\n                new_lid_closed = bool::try_from(value).unwrap_or(new_lid_closed);\n                changed = true;\n            }\n\n            if !changed {\n                continue;\n            }\n\n            if new_lid_closed == lid_closed {\n                continue;\n            }\n\n            lid_closed = new_lid_closed;\n            if let Err(err) = to_niri.send(Login1ToNiri::LidClosedChanged(lid_closed)) {\n                warn!(\"error sending message to niri: {err:?}\");\n                return;\n            };\n        }\n    };\n\n    let task = conn\n        .inner()\n        .executor()\n        .spawn(future, \"monitor login1 property changes\");\n    task.detach();\n\n    Ok(conn)\n}\n"
  },
  {
    "path": "src/dbus/freedesktop_screensaver.rs",
    "content": "use std::collections::hash_map::Entry;\nuse std::collections::HashMap;\nuse std::sync::atomic::{AtomicBool, AtomicU32, Ordering};\nuse std::sync::{Arc, Mutex, OnceLock};\n\nuse anyhow::Context;\nuse futures_util::StreamExt;\nuse zbus::fdo::{self, RequestNameFlags};\nuse zbus::message::Header;\nuse zbus::names::{OwnedUniqueName, UniqueName};\nuse zbus::zvariant::NoneValue;\nuse zbus::{interface, Task};\n\nuse super::Start;\n\n#[derive(Clone)]\npub struct ScreenSaver {\n    is_inhibited: Arc<AtomicBool>,\n    is_broken: Arc<AtomicBool>,\n    inhibitors: Arc<Mutex<HashMap<u32, OwnedUniqueName>>>,\n    counter: Arc<AtomicU32>,\n    monitor_task: Arc<OnceLock<Task<()>>>,\n}\n\n#[interface(name = \"org.freedesktop.ScreenSaver\")]\nimpl ScreenSaver {\n    async fn inhibit(\n        &mut self,\n        #[zbus(header)] hdr: Header<'_>,\n        application_name: &str,\n        reason_for_inhibit: &str,\n    ) -> fdo::Result<u32> {\n        trace!(\n            \"fdo inhibit, app: `{application_name}`, reason: `{reason_for_inhibit}`, owner: {:?}\",\n            hdr.sender()\n        );\n\n        let Some(name) = hdr.sender() else {\n            return Err(fdo::Error::Failed(String::from(\"no sender\")));\n        };\n        let name = OwnedUniqueName::from(name.to_owned());\n\n        let mut inhibitors = self.inhibitors.lock().unwrap();\n\n        let mut cookie = None;\n        for _ in 0..3 {\n            let mut inhibitor_key = self.counter.fetch_add(1, Ordering::SeqCst);\n            if inhibitor_key == 0 {\n                // Some clients don't like 0, add one more.\n                inhibitor_key = self.counter.fetch_add(1, Ordering::SeqCst);\n            }\n\n            if let Entry::Vacant(entry) = inhibitors.entry(inhibitor_key) {\n                entry.insert(name);\n                self.is_inhibited.store(true, Ordering::SeqCst);\n                let _ = cookie.insert(inhibitor_key);\n                break;\n            }\n        }\n\n        cookie.ok_or_else(|| fdo::Error::Failed(String::from(\"no available cookie\")))\n    }\n\n    async fn un_inhibit(&mut self, cookie: u32) -> fdo::Result<()> {\n        trace!(\"fdo uninhibit, cookie: {cookie}\");\n\n        let mut inhibitors = self.inhibitors.lock().unwrap();\n\n        if inhibitors.remove(&cookie).is_some() {\n            if inhibitors.is_empty() {\n                self.is_inhibited.store(false, Ordering::SeqCst);\n            }\n\n            Ok(())\n        } else {\n            Err(fdo::Error::Failed(String::from(\"invalid cookie\")))\n        }\n    }\n}\n\nimpl ScreenSaver {\n    pub fn new(is_inhibited: Arc<AtomicBool>) -> Self {\n        Self {\n            is_inhibited,\n            is_broken: Arc::new(AtomicBool::new(false)),\n            inhibitors: Arc::new(Mutex::new(HashMap::new())),\n            // Start from 1 because some clients don't like 0.\n            counter: Arc::new(AtomicU32::new(1)),\n            monitor_task: Arc::new(OnceLock::new()),\n        }\n    }\n}\n\nasync fn monitor_disappeared_clients(\n    conn: &zbus::Connection,\n    is_inhibited: Arc<AtomicBool>,\n    inhibitors: Arc<Mutex<HashMap<u32, OwnedUniqueName>>>,\n) -> anyhow::Result<()> {\n    let proxy = fdo::DBusProxy::new(conn)\n        .await\n        .context(\"error creating a DBusProxy\")?;\n\n    let mut stream = proxy\n        .receive_name_owner_changed_with_args(&[(2, UniqueName::null_value())])\n        .await\n        .context(\"error creating a NameOwnerChanged stream\")?;\n\n    while let Some(signal) = stream.next().await {\n        let args = signal\n            .args()\n            .context(\"error retrieving NameOwnerChanged args\")?;\n\n        let Some(name) = &**args.old_owner() else {\n            continue;\n        };\n\n        if args.new_owner().is_none() {\n            trace!(\"fdo ScreenSaver client disappeared: {name}\");\n\n            let mut inhibitors = inhibitors.lock().unwrap();\n            inhibitors.retain(|_, owner| owner != name);\n            is_inhibited.store(!inhibitors.is_empty(), Ordering::SeqCst);\n        } else {\n            error!(\"non-null new_owner should've been filtered out\");\n        }\n    }\n\n    Ok(())\n}\n\nimpl Start for ScreenSaver {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {\n        let is_inhibited = self.is_inhibited.clone();\n        let is_broken = self.is_broken.clone();\n        let inhibitors = self.inhibitors.clone();\n        let monitor_task = self.monitor_task.clone();\n\n        let conn = zbus::blocking::Connection::session()?;\n        let flags = RequestNameFlags::AllowReplacement\n            | RequestNameFlags::ReplaceExisting\n            | RequestNameFlags::DoNotQueue;\n\n        let org_fd_ss_registered = conn\n            .object_server()\n            .at(\"/org/freedesktop/ScreenSaver\", self.clone())?;\n        let ss_registered = conn.object_server().at(\"/ScreenSaver\", self)?;\n\n        if !org_fd_ss_registered && !ss_registered {\n            anyhow::bail!(\"failed to register any org.freedesktop.ScreenSaver interface\")\n        }\n\n        conn.request_name_with_flags(\"org.freedesktop.ScreenSaver\", flags)?;\n\n        let async_conn = conn.inner();\n        let future = {\n            let conn = async_conn.clone();\n            async move {\n                if let Err(err) =\n                    monitor_disappeared_clients(&conn, is_inhibited.clone(), inhibitors.clone())\n                        .await\n                {\n                    warn!(\"error monitoring org.freedesktop.ScreenSaver clients: {err:?}\");\n                    is_broken.store(true, Ordering::SeqCst);\n                    is_inhibited.store(false, Ordering::SeqCst);\n                    inhibitors.lock().unwrap().clear();\n                }\n            }\n        };\n        let task = async_conn\n            .executor()\n            .spawn(future, \"monitor disappearing clients\");\n        monitor_task.set(task).unwrap();\n\n        Ok(conn)\n    }\n}\n"
  },
  {
    "path": "src/dbus/gnome_shell_introspect.rs",
    "content": "use std::collections::HashMap;\n\nuse zbus::fdo::{self, RequestNameFlags};\nuse zbus::interface;\nuse zbus::object_server::SignalEmitter;\nuse zbus::zvariant::{SerializeDict, Type, Value};\n\nuse super::Start;\n\npub struct Introspect {\n    to_niri: calloop::channel::Sender<IntrospectToNiri>,\n    from_niri: async_channel::Receiver<NiriToIntrospect>,\n}\n\npub enum IntrospectToNiri {\n    GetWindows,\n}\n\npub enum NiriToIntrospect {\n    Windows(HashMap<u64, WindowProperties>),\n}\n\n#[derive(Debug, SerializeDict, Type, Value)]\n#[zvariant(signature = \"dict\")]\npub struct WindowProperties {\n    /// Window title.\n    pub title: String,\n    /// Window app ID.\n    ///\n    /// This is actually the name of the .desktop file, and Shell does internal tracking to match\n    /// Wayland app IDs to desktop files. We don't do that yet, which is the reason why\n    /// xdg-desktop-portal-gnome's window list is missing icons.\n    #[zvariant(rename = \"app-id\")]\n    pub app_id: String,\n}\n\n#[interface(name = \"org.gnome.Shell.Introspect\")]\nimpl Introspect {\n    async fn get_windows(&self) -> fdo::Result<HashMap<u64, WindowProperties>> {\n        if let Err(err) = self.to_niri.send(IntrospectToNiri::GetWindows) {\n            warn!(\"error sending message to niri: {err:?}\");\n            return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n        }\n\n        match self.from_niri.recv().await {\n            Ok(NiriToIntrospect::Windows(windows)) => Ok(windows),\n            Err(err) => {\n                warn!(\"error receiving message from niri: {err:?}\");\n                Err(fdo::Error::Failed(\"internal error\".to_owned()))\n            }\n        }\n    }\n\n    // FIXME: call this upon window changes, once more of the infrastructure is there (will be\n    // needed for the event stream IPC anyway).\n    #[zbus(signal)]\n    pub async fn windows_changed(ctxt: &SignalEmitter<'_>) -> zbus::Result<()>;\n}\n\nimpl Introspect {\n    pub fn new(\n        to_niri: calloop::channel::Sender<IntrospectToNiri>,\n        from_niri: async_channel::Receiver<NiriToIntrospect>,\n    ) -> Self {\n        Self { to_niri, from_niri }\n    }\n}\n\nimpl Start for Introspect {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {\n        let conn = zbus::blocking::Connection::session()?;\n        let flags = RequestNameFlags::AllowReplacement\n            | RequestNameFlags::ReplaceExisting\n            | RequestNameFlags::DoNotQueue;\n\n        conn.object_server()\n            .at(\"/org/gnome/Shell/Introspect\", self)?;\n        conn.request_name_with_flags(\"org.gnome.Shell.Introspect\", flags)?;\n\n        Ok(conn)\n    }\n}\n"
  },
  {
    "path": "src/dbus/gnome_shell_screenshot.rs",
    "content": "use std::collections::HashMap;\nuse std::path::PathBuf;\n\nuse niri_ipc::PickedColor;\nuse zbus::fdo::{self, RequestNameFlags};\nuse zbus::zvariant::OwnedValue;\nuse zbus::{interface, zvariant};\n\nuse super::Start;\n\npub struct Screenshot {\n    to_niri: calloop::channel::Sender<ScreenshotToNiri>,\n    from_niri: async_channel::Receiver<NiriToScreenshot>,\n}\n\npub enum ScreenshotToNiri {\n    TakeScreenshot { include_cursor: bool },\n    PickColor(async_channel::Sender<Option<PickedColor>>),\n}\n\npub enum NiriToScreenshot {\n    ScreenshotResult(Option<PathBuf>),\n}\n\n#[interface(name = \"org.gnome.Shell.Screenshot\")]\nimpl Screenshot {\n    async fn screenshot(\n        &self,\n        include_cursor: bool,\n        _flash: bool,\n        _filename: PathBuf,\n    ) -> fdo::Result<(bool, PathBuf)> {\n        if let Err(err) = self\n            .to_niri\n            .send(ScreenshotToNiri::TakeScreenshot { include_cursor })\n        {\n            warn!(\"error sending message to niri: {err:?}\");\n            return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n        }\n\n        let filename = match self.from_niri.recv().await {\n            Ok(NiriToScreenshot::ScreenshotResult(Some(filename))) => filename,\n            Ok(NiriToScreenshot::ScreenshotResult(None)) => {\n                return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n            }\n            Err(err) => {\n                warn!(\"error receiving message from niri: {err:?}\");\n                return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n            }\n        };\n\n        Ok((true, filename))\n    }\n\n    async fn pick_color(&self) -> fdo::Result<HashMap<String, OwnedValue>> {\n        let (tx, rx) = async_channel::bounded(1);\n        if let Err(err) = self.to_niri.send(ScreenshotToNiri::PickColor(tx)) {\n            warn!(\"error sending pick color message to niri: {err:?}\");\n            return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n        }\n\n        let color = match rx.recv().await {\n            Ok(Some(color)) => color,\n            Ok(None) => {\n                return Err(fdo::Error::Failed(\"no color picked\".to_owned()));\n            }\n            Err(err) => {\n                warn!(\"error receiving message from niri: {err:?}\");\n                return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n            }\n        };\n\n        let mut result = HashMap::new();\n        let [r, g, b] = color.rgb;\n        result.insert(\n            \"color\".to_string(),\n            zvariant::OwnedValue::try_from(zvariant::Value::from((r, g, b))).unwrap(),\n        );\n\n        Ok(result)\n    }\n}\n\nimpl Screenshot {\n    pub fn new(\n        to_niri: calloop::channel::Sender<ScreenshotToNiri>,\n        from_niri: async_channel::Receiver<NiriToScreenshot>,\n    ) -> Self {\n        Self { to_niri, from_niri }\n    }\n}\n\nimpl Start for Screenshot {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {\n        let conn = zbus::blocking::Connection::session()?;\n        let flags = RequestNameFlags::AllowReplacement\n            | RequestNameFlags::ReplaceExisting\n            | RequestNameFlags::DoNotQueue;\n\n        conn.object_server()\n            .at(\"/org/gnome/Shell/Screenshot\", self)?;\n        conn.request_name_with_flags(\"org.gnome.Shell.Screenshot\", flags)?;\n\n        Ok(conn)\n    }\n}\n"
  },
  {
    "path": "src/dbus/mod.rs",
    "content": "use zbus::blocking::Connection;\nuse zbus::object_server::Interface;\n\nuse crate::niri::State;\n\npub mod freedesktop_a11y;\npub mod freedesktop_locale1;\npub mod freedesktop_login1;\npub mod freedesktop_screensaver;\npub mod gnome_shell_introspect;\npub mod gnome_shell_screenshot;\npub mod mutter_display_config;\npub mod mutter_service_channel;\n\n#[cfg(feature = \"xdp-gnome-screencast\")]\npub mod mutter_screen_cast;\n#[cfg(feature = \"xdp-gnome-screencast\")]\nuse mutter_screen_cast::ScreenCast;\n\nuse self::freedesktop_a11y::KeyboardMonitor;\nuse self::freedesktop_screensaver::ScreenSaver;\nuse self::gnome_shell_introspect::Introspect;\nuse self::mutter_display_config::DisplayConfig;\nuse self::mutter_service_channel::ServiceChannel;\n\ntrait Start: Interface {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection>;\n}\n\n#[derive(Default)]\npub struct DBusServers {\n    pub conn_service_channel: Option<Connection>,\n    pub conn_display_config: Option<Connection>,\n    pub conn_screen_saver: Option<Connection>,\n    pub conn_screen_shot: Option<Connection>,\n    pub conn_introspect: Option<Connection>,\n    #[cfg(feature = \"xdp-gnome-screencast\")]\n    pub conn_screen_cast: Option<Connection>,\n    pub conn_login1: Option<Connection>,\n    pub conn_locale1: Option<Connection>,\n    pub conn_keyboard_monitor: Option<Connection>,\n}\n\nimpl DBusServers {\n    pub fn start(state: &mut State, is_session_instance: bool) {\n        let _span = tracy_client::span!(\"DBusServers::start\");\n\n        let backend = &state.backend;\n        let niri = &mut state.niri;\n        let config = niri.config.borrow();\n\n        let mut dbus = Self::default();\n\n        if is_session_instance {\n            let (to_niri, from_service_channel) = calloop::channel::channel();\n            let service_channel = ServiceChannel::new(to_niri);\n            niri.event_loop\n                .insert_source(from_service_channel, move |event, _, state| match event {\n                    calloop::channel::Event::Msg(new_client) => {\n                        state.niri.insert_client(new_client);\n                    }\n                    calloop::channel::Event::Closed => (),\n                })\n                .unwrap();\n            dbus.conn_service_channel = try_start(service_channel);\n        }\n\n        if is_session_instance || config.debug.dbus_interfaces_in_non_session_instances {\n            let (to_niri, from_display_config) = calloop::channel::channel();\n            let display_config = DisplayConfig::new(to_niri, backend.ipc_outputs());\n            niri.event_loop\n                .insert_source(from_display_config, move |event, _, state| match event {\n                    calloop::channel::Event::Msg(new_conf) => {\n                        for (name, conf) in new_conf {\n                            state.modify_output_config(&name, move |output| {\n                                if let Some(new_output) = conf {\n                                    *output = new_output;\n                                } else {\n                                    output.off = true;\n                                }\n                            });\n                        }\n                        state.reload_output_config();\n                    }\n                    calloop::channel::Event::Closed => (),\n                })\n                .unwrap();\n            dbus.conn_display_config = try_start(display_config);\n\n            let screen_saver = ScreenSaver::new(niri.is_fdo_idle_inhibited.clone());\n            dbus.conn_screen_saver = try_start(screen_saver);\n\n            let (to_niri, from_screenshot) = calloop::channel::channel();\n            let (to_screenshot, from_niri) = async_channel::unbounded();\n            niri.event_loop\n                .insert_source(from_screenshot, move |event, _, state| match event {\n                    calloop::channel::Event::Msg(msg) => {\n                        state.on_screen_shot_msg(&to_screenshot, msg)\n                    }\n                    calloop::channel::Event::Closed => (),\n                })\n                .unwrap();\n            let screenshot = gnome_shell_screenshot::Screenshot::new(to_niri, from_niri);\n            dbus.conn_screen_shot = try_start(screenshot);\n\n            let (to_niri, from_introspect) = calloop::channel::channel();\n            let (to_introspect, from_niri) = async_channel::unbounded();\n            niri.event_loop\n                .insert_source(from_introspect, move |event, _, state| match event {\n                    calloop::channel::Event::Msg(msg) => {\n                        state.on_introspect_msg(&to_introspect, msg)\n                    }\n                    calloop::channel::Event::Closed => (),\n                })\n                .unwrap();\n            let introspect = Introspect::new(to_niri, from_niri);\n            dbus.conn_introspect = try_start(introspect);\n\n            #[cfg(feature = \"xdp-gnome-screencast\")]\n            {\n                let (to_niri, from_screen_cast) = calloop::channel::channel();\n                niri.event_loop\n                    .insert_source(from_screen_cast, {\n                        move |event, _, state| match event {\n                            calloop::channel::Event::Msg(msg) => state.on_screen_cast_msg(msg),\n                            calloop::channel::Event::Closed => (),\n                        }\n                    })\n                    .unwrap();\n                let screen_cast = ScreenCast::new(backend.ipc_outputs(), to_niri);\n                dbus.conn_screen_cast = try_start(screen_cast);\n            }\n\n            let keyboard_monitor = KeyboardMonitor::new();\n            if let Some(x) = try_start(keyboard_monitor.clone()) {\n                dbus.conn_keyboard_monitor = Some(x);\n                niri.a11y_keyboard_monitor = Some(keyboard_monitor);\n            }\n        }\n\n        let (to_niri, from_login1) = calloop::channel::channel();\n        niri.event_loop\n            .insert_source(from_login1, move |event, _, state| match event {\n                calloop::channel::Event::Msg(msg) => state.on_login1_msg(msg),\n                calloop::channel::Event::Closed => (),\n            })\n            .unwrap();\n        match freedesktop_login1::start(to_niri) {\n            Ok(conn) => {\n                dbus.conn_login1 = Some(conn);\n            }\n            Err(err) => {\n                warn!(\"error starting login1 watcher: {err:?}\");\n            }\n        }\n\n        let (to_niri, from_locale1) = calloop::channel::channel();\n        niri.event_loop\n            .insert_source(from_locale1, move |event, _, state| match event {\n                calloop::channel::Event::Msg(msg) => state.on_locale1_msg(msg),\n                calloop::channel::Event::Closed => (),\n            })\n            .unwrap();\n        match freedesktop_locale1::start(to_niri) {\n            Ok(conn) => {\n                dbus.conn_locale1 = Some(conn);\n            }\n            Err(err) => {\n                warn!(\"error starting locale1 watcher: {err:?}\");\n            }\n        }\n\n        niri.dbus = Some(dbus);\n    }\n}\n\nfn try_start<I: Start>(iface: I) -> Option<Connection> {\n    match iface.start() {\n        Ok(conn) => Some(conn),\n        Err(err) => {\n            warn!(\"error starting {}: {err:?}\", I::name());\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "src/dbus/mutter_display_config.rs",
    "content": "use std::collections::HashMap;\nuse std::str::FromStr;\nuse std::sync::{Arc, Mutex};\n\nuse serde::{Deserialize, Serialize};\nuse smithay::utils::Size;\nuse zbus::fdo::RequestNameFlags;\nuse zbus::object_server::SignalEmitter;\nuse zbus::zvariant::{self, OwnedValue, Type};\nuse zbus::{fdo, interface};\n\nuse super::Start;\nuse crate::backend::IpcOutputMap;\nuse crate::utils::is_laptop_panel;\nuse crate::utils::scale::supported_scales;\n\npub struct DisplayConfig {\n    to_niri: calloop::channel::Sender<HashMap<String, Option<niri_config::Output>>>,\n    ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n}\n\n#[derive(Serialize, Type)]\npub struct Monitor {\n    names: (String, String, String, String),\n    modes: Vec<Mode>,\n    properties: HashMap<String, OwnedValue>,\n}\n\n#[derive(Serialize, Type)]\npub struct Mode {\n    id: String,\n    width: i32,\n    height: i32,\n    refresh_rate: f64,\n    preferred_scale: f64,\n    supported_scales: Vec<f64>,\n    properties: HashMap<String, OwnedValue>,\n}\n\n#[derive(Serialize, Type)]\npub struct LogicalMonitor {\n    x: i32,\n    y: i32,\n    scale: f64,\n    transform: u32,\n    is_primary: bool,\n    monitors: Vec<(String, String, String, String)>,\n    properties: HashMap<String, OwnedValue>,\n}\n\n// ApplyMonitorsConfig\n#[derive(Deserialize, Type)]\npub struct LogicalMonitorConfiguration {\n    x: i32,\n    y: i32,\n    scale: f64,\n    transform: u32,\n    _is_primary: bool,\n    monitors: Vec<(String, String, HashMap<String, OwnedValue>)>,\n}\n\n#[interface(name = \"org.gnome.Mutter.DisplayConfig\")]\nimpl DisplayConfig {\n    async fn get_current_state(\n        &self,\n    ) -> fdo::Result<(\n        u32,\n        Vec<Monitor>,\n        Vec<LogicalMonitor>,\n        HashMap<String, OwnedValue>,\n    )> {\n        // Construct the DBus response.\n        let mut monitors = Vec::new();\n        let mut logical_monitors = Vec::new();\n\n        for output in self.ipc_outputs.lock().unwrap().values() {\n            // Loosely matches the check in Mutter.\n            let c = &output.name;\n            let is_laptop_panel = is_laptop_panel(c);\n            let display_name = make_display_name(output, is_laptop_panel);\n\n            let mut properties = HashMap::new();\n            properties.insert(\n                String::from(\"display-name\"),\n                OwnedValue::from(zvariant::Str::from(display_name)),\n            );\n            properties.insert(\n                String::from(\"is-builtin\"),\n                OwnedValue::from(is_laptop_panel),\n            );\n\n            let mut modes: Vec<Mode> = output\n                .modes\n                .iter()\n                .map(|m| {\n                    let niri_ipc::Mode {\n                        width,\n                        height,\n                        refresh_rate,\n                        is_preferred,\n                    } = *m;\n                    let width = i32::from(width);\n                    let height = i32::from(height);\n                    let refresh_rate = refresh_rate as f64 / 1000.;\n\n                    Mode {\n                        id: format!(\"{width}x{height}@{refresh_rate:.3}\"),\n                        width,\n                        height,\n                        refresh_rate,\n                        preferred_scale: 1.,\n                        supported_scales: supported_scales(Size::from((width, height))).collect(),\n                        properties: HashMap::from([(\n                            String::from(\"is-preferred\"),\n                            OwnedValue::from(is_preferred),\n                        )]),\n                    }\n                })\n                .collect();\n            if let Some(mode) = output.current_mode {\n                modes[mode]\n                    .properties\n                    .insert(String::from(\"is-current\"), OwnedValue::from(true));\n            }\n\n            let connector = c.clone();\n            let model = output.model.clone();\n            let make = output.make.clone();\n\n            // Serial is used for session restore, so fall back to the connector name if it's\n            // not available.\n            let serial = output.serial.as_ref().unwrap_or(&connector).clone();\n\n            let names = (connector, make, model, serial);\n\n            if let Some(logical) = output.logical.as_ref() {\n                let transform = match logical.transform {\n                    niri_ipc::Transform::Normal => 0,\n                    niri_ipc::Transform::_90 => 1,\n                    niri_ipc::Transform::_180 => 2,\n                    niri_ipc::Transform::_270 => 3,\n                    niri_ipc::Transform::Flipped => 4,\n                    niri_ipc::Transform::Flipped90 => 5,\n                    niri_ipc::Transform::Flipped180 => 6,\n                    niri_ipc::Transform::Flipped270 => 7,\n                };\n\n                logical_monitors.push(LogicalMonitor {\n                    x: logical.x,\n                    y: logical.y,\n                    scale: logical.scale,\n                    transform,\n                    is_primary: false,\n                    monitors: vec![names.clone()],\n                    properties: HashMap::new(),\n                });\n            }\n\n            monitors.push(Monitor {\n                names,\n                modes,\n                properties,\n            });\n        }\n\n        // Sort by connector.\n        monitors.sort_unstable_by(|a, b| a.names.0.cmp(&b.names.0));\n        logical_monitors.sort_unstable_by(|a, b| a.monitors[0].0.cmp(&b.monitors[0].0));\n\n        let properties = HashMap::from([(String::from(\"layout-mode\"), OwnedValue::from(1u32))]);\n        Ok((0, monitors, logical_monitors, properties))\n    }\n\n    async fn apply_monitors_config(\n        &self,\n        _serial: u32,\n        method: u32,\n        logical_monitor_configs: Vec<LogicalMonitorConfiguration>,\n        _properties: HashMap<String, OwnedValue>,\n    ) -> fdo::Result<()> {\n        let current_conf = self.ipc_outputs.lock().unwrap();\n        let mut new_conf = HashMap::new();\n        for requested_config in logical_monitor_configs {\n            if requested_config.monitors.len() > 1 {\n                return Err(zbus::fdo::Error::Failed(\n                    \"Mirroring is not yet supported\".to_owned(),\n                ));\n            }\n            for (connector, mode, _props) in requested_config.monitors {\n                if !current_conf.values().any(|o| o.name == connector) {\n                    return Err(zbus::fdo::Error::Failed(format!(\n                        \"Connector '{connector}' not found\",\n                    )));\n                }\n                new_conf.insert(\n                    connector.clone(),\n                    Some(niri_config::Output {\n                        off: false,\n                        name: connector,\n                        scale: Some(niri_config::FloatOrInt(requested_config.scale)),\n                        transform: match requested_config.transform {\n                            0 => niri_ipc::Transform::Normal,\n                            1 => niri_ipc::Transform::_90,\n                            2 => niri_ipc::Transform::_180,\n                            3 => niri_ipc::Transform::_270,\n                            4 => niri_ipc::Transform::Flipped,\n                            5 => niri_ipc::Transform::Flipped90,\n                            6 => niri_ipc::Transform::Flipped180,\n                            7 => niri_ipc::Transform::Flipped270,\n                            x => {\n                                return Err(zbus::fdo::Error::Failed(format!(\n                                    \"Unknown transform {x}\",\n                                )))\n                            }\n                        },\n                        position: Some(niri_config::Position {\n                            x: requested_config.x,\n                            y: requested_config.y,\n                        }),\n                        mode: Some(niri_config::output::Mode {\n                            custom: false,\n                            mode: niri_ipc::ConfiguredMode::from_str(&mode).map_err(|e| {\n                                zbus::fdo::Error::Failed(format!(\n                                    \"Could not parse mode '{mode}': {e}\"\n                                ))\n                            })?,\n                        }),\n                        // FIXME: VRR\n                        ..Default::default()\n                    }),\n                );\n            }\n        }\n        if new_conf.is_empty() {\n            return Err(zbus::fdo::Error::Failed(\n                \"At least one output must be enabled\".to_owned(),\n            ));\n        }\n        for output in current_conf.values() {\n            if !new_conf.contains_key(&output.name) {\n                new_conf.insert(output.name.clone(), None);\n            }\n        }\n        if method == 0 {\n            // 0 means \"verify\", so don't actually apply here\n            return Ok(());\n        }\n        if let Err(err) = self.to_niri.send(new_conf) {\n            warn!(\"error sending message to niri: {err:?}\");\n            return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n        }\n        Ok(())\n    }\n\n    #[zbus(signal)]\n    pub async fn monitors_changed(ctxt: &SignalEmitter<'_>) -> zbus::Result<()>;\n\n    #[zbus(property)]\n    fn power_save_mode(&self) -> i32 {\n        -1\n    }\n\n    #[zbus(property)]\n    fn set_power_save_mode(&self, _mode: i32) -> zbus::Result<()> {\n        Err(zbus::Error::Unsupported)\n    }\n\n    #[zbus(property)]\n    fn panel_orientation_managed(&self) -> bool {\n        false\n    }\n\n    #[zbus(property)]\n    fn apply_monitors_config_allowed(&self) -> bool {\n        true\n    }\n\n    #[zbus(property)]\n    fn night_light_supported(&self) -> bool {\n        false\n    }\n}\n\nimpl DisplayConfig {\n    pub fn new(\n        to_niri: calloop::channel::Sender<HashMap<String, Option<niri_config::Output>>>,\n        ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n    ) -> Self {\n        Self {\n            to_niri,\n            ipc_outputs,\n        }\n    }\n}\n\nimpl Start for DisplayConfig {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {\n        let conn = zbus::blocking::Connection::session()?;\n        let flags = RequestNameFlags::AllowReplacement\n            | RequestNameFlags::ReplaceExisting\n            | RequestNameFlags::DoNotQueue;\n\n        conn.object_server()\n            .at(\"/org/gnome/Mutter/DisplayConfig\", self)?;\n        conn.request_name_with_flags(\"org.gnome.Mutter.DisplayConfig\", flags)?;\n\n        Ok(conn)\n    }\n}\n\n// Adapted from Mutter.\nfn make_display_name(output: &niri_ipc::Output, is_laptop_panel: bool) -> String {\n    if is_laptop_panel {\n        return String::from(\"Built-in display\");\n    }\n\n    let make = &output.make;\n    let model = &output.model;\n    if let Some(diagonal) = output.physical_size.map(|(width_mm, height_mm)| {\n        let diagonal = f64::hypot(f64::from(width_mm), f64::from(height_mm)) / 25.4;\n        format_diagonal(diagonal)\n    }) {\n        format!(\"{make} {diagonal}\")\n    } else if model != \"Unknown\" {\n        format!(\"{make} {model}\")\n    } else {\n        make.clone()\n    }\n}\n\nfn format_diagonal(diagonal_inches: f64) -> String {\n    let known = [12.1, 13.3, 15.6];\n    if let Some(d) = known.iter().find(|d| (*d - diagonal_inches).abs() < 0.1) {\n        format!(\"{d:.1}″\")\n    } else {\n        format!(\"{}″\", diagonal_inches.round() as u32)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::assert_snapshot;\n\n    use super::*;\n\n    #[test]\n    fn test_format_diagonal() {\n        assert_snapshot!(format_diagonal(12.11), @\"12.1″\");\n        assert_snapshot!(format_diagonal(13.28), @\"13.3″\");\n        assert_snapshot!(format_diagonal(15.6), @\"15.6″\");\n        assert_snapshot!(format_diagonal(23.2), @\"23″\");\n        assert_snapshot!(format_diagonal(24.8), @\"25″\");\n    }\n}\n"
  },
  {
    "path": "src/dbus/mutter_screen_cast.rs",
    "content": "use std::collections::HashMap;\nuse std::mem;\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::{Arc, Mutex};\n\nuse serde::Deserialize;\nuse zbus::fdo::RequestNameFlags;\nuse zbus::object_server::{InterfaceRef, SignalEmitter};\nuse zbus::zvariant::{DeserializeDict, OwnedObjectPath, SerializeDict, Type, Value};\nuse zbus::{fdo, interface, ObjectServer};\n\nuse super::Start;\nuse crate::backend::IpcOutputMap;\nuse crate::utils::{CastSessionId, CastStreamId};\n\n#[derive(Clone)]\npub struct ScreenCast {\n    ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n    to_niri: calloop::channel::Sender<ScreenCastToNiri>,\n    #[allow(clippy::type_complexity)]\n    sessions: Arc<Mutex<Vec<(Session, InterfaceRef<Session>)>>>,\n}\n\n#[derive(Clone)]\npub struct Session {\n    id: CastSessionId,\n    ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n    to_niri: calloop::channel::Sender<ScreenCastToNiri>,\n    #[allow(clippy::type_complexity)]\n    streams: Arc<Mutex<Vec<(Stream, InterfaceRef<Stream>)>>>,\n    stopped: Arc<AtomicBool>,\n}\n\n#[derive(Debug, Default, Deserialize, Type, Clone, Copy, PartialEq, Eq)]\npub enum CursorMode {\n    #[default]\n    Hidden = 0,\n    Embedded = 1,\n    Metadata = 2,\n}\n\n#[derive(Debug, DeserializeDict, Type)]\n#[zvariant(signature = \"dict\")]\nstruct RecordMonitorProperties {\n    #[zvariant(rename = \"cursor-mode\")]\n    cursor_mode: Option<CursorMode>,\n    #[zvariant(rename = \"is-recording\")]\n    _is_recording: Option<bool>,\n}\n\n#[derive(Debug, DeserializeDict, Type)]\n#[zvariant(signature = \"dict\")]\nstruct RecordWindowProperties {\n    #[zvariant(rename = \"window-id\")]\n    window_id: u64,\n    #[zvariant(rename = \"cursor-mode\")]\n    cursor_mode: Option<CursorMode>,\n    #[zvariant(rename = \"is-recording\")]\n    _is_recording: Option<bool>,\n}\n\n#[derive(Clone)]\npub struct Stream {\n    id: CastStreamId,\n    session_id: CastSessionId,\n    target: StreamTarget,\n    cursor_mode: CursorMode,\n    was_started: Arc<AtomicBool>,\n    to_niri: calloop::channel::Sender<ScreenCastToNiri>,\n}\n\n#[derive(Clone)]\nenum StreamTarget {\n    // FIXME: update on scale changes and whatnot.\n    Output(niri_ipc::Output),\n    Window { id: u64 },\n}\n\n#[derive(Debug, Clone)]\npub enum StreamTargetId {\n    Output { name: String },\n    Window { id: u64 },\n}\n\n#[derive(Debug, SerializeDict, Type, Value)]\n#[zvariant(signature = \"dict\")]\nstruct StreamParameters {\n    /// Position of the stream in logical coordinates.\n    position: (i32, i32),\n    /// Size of the stream in logical coordinates.\n    size: (i32, i32),\n}\n\npub enum ScreenCastToNiri {\n    StartCast {\n        session_id: CastSessionId,\n        stream_id: CastStreamId,\n        target: StreamTargetId,\n        cursor_mode: CursorMode,\n        signal_ctx: SignalEmitter<'static>,\n    },\n    StopCast {\n        session_id: CastSessionId,\n    },\n}\n\n#[interface(name = \"org.gnome.Mutter.ScreenCast\")]\nimpl ScreenCast {\n    async fn create_session(\n        &self,\n        #[zbus(object_server)] server: &ObjectServer,\n        properties: HashMap<&str, Value<'_>>,\n    ) -> fdo::Result<OwnedObjectPath> {\n        if properties.contains_key(\"remote-desktop-session-id\") {\n            return Err(fdo::Error::Failed(\n                \"there are no remote desktop sessions\".to_owned(),\n            ));\n        }\n\n        let session_id = CastSessionId::next();\n        let path = format!(\"/org/gnome/Mutter/ScreenCast/Session/u{}\", session_id.get());\n        let path = OwnedObjectPath::try_from(path).unwrap();\n\n        let session = Session::new(session_id, self.ipc_outputs.clone(), self.to_niri.clone());\n        match server.at(&path, session.clone()).await {\n            Ok(true) => {\n                let iface = server.interface(&path).await.unwrap();\n                self.sessions.lock().unwrap().push((session, iface));\n            }\n            Ok(false) => return Err(fdo::Error::Failed(\"session path already exists\".to_owned())),\n            Err(err) => {\n                return Err(fdo::Error::Failed(format!(\n                    \"error creating session object: {err:?}\"\n                )))\n            }\n        }\n\n        Ok(path)\n    }\n\n    #[zbus(property)]\n    async fn version(&self) -> i32 {\n        4\n    }\n}\n\n#[interface(name = \"org.gnome.Mutter.ScreenCast.Session\")]\nimpl Session {\n    async fn start(&self) {\n        debug!(\"start\");\n\n        for (stream, iface) in &*self.streams.lock().unwrap() {\n            stream.start(iface.signal_emitter().clone());\n        }\n    }\n\n    pub async fn stop(\n        &self,\n        #[zbus(object_server)] server: &ObjectServer,\n        #[zbus(signal_context)] ctxt: SignalEmitter<'_>,\n    ) {\n        debug!(\"stop\");\n\n        if self.stopped.swap(true, Ordering::SeqCst) {\n            // Already stopped.\n            return;\n        }\n\n        Session::closed(&ctxt).await.unwrap();\n\n        if let Err(err) = self.to_niri.send(ScreenCastToNiri::StopCast {\n            session_id: self.id,\n        }) {\n            warn!(\"error sending StopCast to niri: {err:?}\");\n        }\n\n        let streams = mem::take(&mut *self.streams.lock().unwrap());\n        for (_, iface) in streams.iter() {\n            server\n                .remove::<Stream, _>(iface.signal_emitter().path())\n                .await\n                .unwrap();\n        }\n\n        server.remove::<Session, _>(ctxt.path()).await.unwrap();\n    }\n\n    async fn record_monitor(\n        &mut self,\n        #[zbus(object_server)] server: &ObjectServer,\n        connector: &str,\n        properties: RecordMonitorProperties,\n    ) -> fdo::Result<OwnedObjectPath> {\n        debug!(connector, ?properties, \"record_monitor\");\n\n        let output = {\n            let ipc_outputs = self.ipc_outputs.lock().unwrap();\n            ipc_outputs.values().find(|o| o.name == connector).cloned()\n        };\n        let Some(output) = output else {\n            return Err(fdo::Error::Failed(\"no such monitor\".to_owned()));\n        };\n\n        if output.logical.is_none() {\n            return Err(fdo::Error::Failed(\"monitor is disabled\".to_owned()));\n        }\n\n        let stream_id = CastStreamId::next();\n        let path = format!(\"/org/gnome/Mutter/ScreenCast/Stream/u{}\", stream_id.get());\n        let path = OwnedObjectPath::try_from(path).unwrap();\n\n        let cursor_mode = properties.cursor_mode.unwrap_or_default();\n\n        let target = StreamTarget::Output(output);\n        let stream = Stream::new(\n            stream_id,\n            self.id,\n            target,\n            cursor_mode,\n            self.to_niri.clone(),\n        );\n        match server.at(&path, stream.clone()).await {\n            Ok(true) => {\n                let iface = server.interface(&path).await.unwrap();\n                self.streams.lock().unwrap().push((stream, iface));\n            }\n            Ok(false) => return Err(fdo::Error::Failed(\"stream path already exists\".to_owned())),\n            Err(err) => {\n                return Err(fdo::Error::Failed(format!(\n                    \"error creating stream object: {err:?}\"\n                )))\n            }\n        }\n\n        Ok(path)\n    }\n\n    async fn record_window(\n        &mut self,\n        #[zbus(object_server)] server: &ObjectServer,\n        properties: RecordWindowProperties,\n    ) -> fdo::Result<OwnedObjectPath> {\n        debug!(?properties, \"record_window\");\n\n        let stream_id = CastStreamId::next();\n        let path = format!(\"/org/gnome/Mutter/ScreenCast/Stream/u{}\", stream_id.get());\n        let path = OwnedObjectPath::try_from(path).unwrap();\n\n        let cursor_mode = properties.cursor_mode.unwrap_or_default();\n\n        let target = StreamTarget::Window {\n            id: properties.window_id,\n        };\n        let stream = Stream::new(\n            stream_id,\n            self.id,\n            target,\n            cursor_mode,\n            self.to_niri.clone(),\n        );\n        match server.at(&path, stream.clone()).await {\n            Ok(true) => {\n                let iface = server.interface(&path).await.unwrap();\n                self.streams.lock().unwrap().push((stream, iface));\n            }\n            Ok(false) => return Err(fdo::Error::Failed(\"stream path already exists\".to_owned())),\n            Err(err) => {\n                return Err(fdo::Error::Failed(format!(\n                    \"error creating stream object: {err:?}\"\n                )))\n            }\n        }\n\n        Ok(path)\n    }\n\n    #[zbus(signal)]\n    async fn closed(ctxt: &SignalEmitter<'_>) -> zbus::Result<()>;\n}\n\n#[interface(name = \"org.gnome.Mutter.ScreenCast.Stream\")]\nimpl Stream {\n    #[zbus(signal)]\n    pub async fn pipe_wire_stream_added(ctxt: &SignalEmitter<'_>, node_id: u32)\n        -> zbus::Result<()>;\n\n    #[zbus(property)]\n    async fn parameters(&self) -> StreamParameters {\n        match &self.target {\n            StreamTarget::Output(output) => {\n                let logical = output.logical.as_ref().unwrap();\n                StreamParameters {\n                    position: (logical.x, logical.y),\n                    size: (logical.width as i32, logical.height as i32),\n                }\n            }\n            StreamTarget::Window { .. } => {\n                // Does any consumer need this?\n                StreamParameters {\n                    position: (0, 0),\n                    size: (1, 1),\n                }\n            }\n        }\n    }\n}\n\nimpl ScreenCast {\n    pub fn new(\n        ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n        to_niri: calloop::channel::Sender<ScreenCastToNiri>,\n    ) -> Self {\n        Self {\n            ipc_outputs,\n            to_niri,\n            sessions: Arc::new(Mutex::new(vec![])),\n        }\n    }\n}\n\nimpl Start for ScreenCast {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {\n        let conn = zbus::blocking::Connection::session()?;\n        let flags = RequestNameFlags::AllowReplacement\n            | RequestNameFlags::ReplaceExisting\n            | RequestNameFlags::DoNotQueue;\n\n        conn.object_server()\n            .at(\"/org/gnome/Mutter/ScreenCast\", self)?;\n        conn.request_name_with_flags(\"org.gnome.Mutter.ScreenCast\", flags)?;\n\n        Ok(conn)\n    }\n}\n\nimpl Session {\n    pub fn new(\n        id: CastSessionId,\n        ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n        to_niri: calloop::channel::Sender<ScreenCastToNiri>,\n    ) -> Self {\n        Self {\n            id,\n            ipc_outputs,\n            streams: Arc::new(Mutex::new(vec![])),\n            to_niri,\n            stopped: Arc::new(AtomicBool::new(false)),\n        }\n    }\n}\n\nimpl Drop for Session {\n    fn drop(&mut self) {\n        let _ = self.to_niri.send(ScreenCastToNiri::StopCast {\n            session_id: self.id,\n        });\n    }\n}\n\nimpl Stream {\n    fn new(\n        id: CastStreamId,\n        session_id: CastSessionId,\n        target: StreamTarget,\n        cursor_mode: CursorMode,\n        to_niri: calloop::channel::Sender<ScreenCastToNiri>,\n    ) -> Self {\n        Self {\n            id,\n            session_id,\n            target,\n            cursor_mode,\n            was_started: Arc::new(AtomicBool::new(false)),\n            to_niri,\n        }\n    }\n\n    fn start(&self, ctxt: SignalEmitter<'static>) {\n        if self.was_started.load(Ordering::SeqCst) {\n            return;\n        }\n\n        let msg = ScreenCastToNiri::StartCast {\n            session_id: self.session_id,\n            stream_id: self.id,\n            target: self.target.make_id(),\n            cursor_mode: self.cursor_mode,\n            signal_ctx: ctxt,\n        };\n\n        if let Err(err) = self.to_niri.send(msg) {\n            warn!(\"error sending StartCast to niri: {err:?}\");\n        }\n    }\n}\n\nimpl StreamTarget {\n    fn make_id(&self) -> StreamTargetId {\n        match self {\n            StreamTarget::Output(output) => StreamTargetId::Output {\n                name: output.name.clone(),\n            },\n            StreamTarget::Window { id } => StreamTargetId::Window { id: *id },\n        }\n    }\n}\n"
  },
  {
    "path": "src/dbus/mutter_service_channel.rs",
    "content": "use std::os::unix::net::UnixStream;\n\nuse zbus::{fdo, interface, zvariant};\n\nuse super::Start;\nuse crate::niri::NewClient;\n\npub struct ServiceChannel {\n    to_niri: calloop::channel::Sender<NewClient>,\n}\n\n#[interface(name = \"org.gnome.Mutter.ServiceChannel\")]\nimpl ServiceChannel {\n    async fn open_wayland_service_connection(\n        &mut self,\n        service_client_type: u32,\n    ) -> fdo::Result<zvariant::OwnedFd> {\n        if service_client_type != 1 {\n            return Err(fdo::Error::InvalidArgs(\n                \"Invalid service client type\".to_owned(),\n            ));\n        }\n\n        let (sock1, sock2) = UnixStream::pair().unwrap();\n        let client = NewClient {\n            client: sock2,\n            restricted: false,\n            // FIXME: maybe you can get the PID from D-Bus somehow?\n            credentials_unknown: true,\n        };\n        if let Err(err) = self.to_niri.send(client) {\n            warn!(\"error sending message to niri: {err:?}\");\n            return Err(fdo::Error::Failed(\"internal error\".to_owned()));\n        }\n\n        Ok(zvariant::OwnedFd::from(std::os::fd::OwnedFd::from(sock1)))\n    }\n}\n\nimpl ServiceChannel {\n    pub fn new(to_niri: calloop::channel::Sender<NewClient>) -> Self {\n        Self { to_niri }\n    }\n}\n\nimpl Start for ServiceChannel {\n    fn start(self) -> anyhow::Result<zbus::blocking::Connection> {\n        let conn = zbus::blocking::connection::Builder::session()?\n            .name(\"org.gnome.Mutter.ServiceChannel\")?\n            .serve_at(\"/org/gnome/Mutter/ServiceChannel\", self)?\n            .build()?;\n        Ok(conn)\n    }\n}\n"
  },
  {
    "path": "src/frame_clock.rs",
    "content": "use std::num::NonZeroU64;\nuse std::time::Duration;\n\nuse crate::utils::get_monotonic_time;\n\n#[derive(Debug)]\npub struct FrameClock {\n    last_presentation_time: Option<Duration>,\n    refresh_interval_ns: Option<NonZeroU64>,\n    vrr: bool,\n}\n\nimpl FrameClock {\n    pub fn new(refresh_interval: Option<Duration>, vrr: bool) -> Self {\n        let refresh_interval_ns = if let Some(interval) = &refresh_interval {\n            assert_eq!(interval.as_secs(), 0);\n            Some(NonZeroU64::new(interval.subsec_nanos().into()).unwrap())\n        } else {\n            None\n        };\n\n        Self {\n            last_presentation_time: None,\n            refresh_interval_ns,\n            vrr,\n        }\n    }\n\n    pub fn refresh_interval(&self) -> Option<Duration> {\n        self.refresh_interval_ns\n            .map(|r| Duration::from_nanos(r.get()))\n    }\n\n    pub fn set_vrr(&mut self, vrr: bool) {\n        if self.vrr == vrr {\n            return;\n        }\n\n        self.vrr = vrr;\n        self.last_presentation_time = None;\n    }\n\n    pub fn vrr(&self) -> bool {\n        self.vrr\n    }\n\n    pub fn presented(&mut self, presentation_time: Duration) {\n        if presentation_time.is_zero() {\n            // Not interested in these.\n            return;\n        }\n\n        self.last_presentation_time = Some(presentation_time);\n    }\n\n    pub fn next_presentation_time(&self) -> Duration {\n        let mut now = get_monotonic_time();\n\n        let Some(refresh_interval_ns) = self.refresh_interval_ns else {\n            return now;\n        };\n        let Some(last_presentation_time) = self.last_presentation_time else {\n            return now;\n        };\n\n        let refresh_interval_ns = refresh_interval_ns.get();\n\n        if now <= last_presentation_time {\n            // Got an early VBlank.\n            let orig_now = now;\n            now += Duration::from_nanos(refresh_interval_ns);\n\n            if now < last_presentation_time {\n                // Not sure when this can happen.\n                error!(\n                    now = ?orig_now,\n                    ?last_presentation_time,\n                    \"got a 2+ early VBlank, {:?} until presentation\",\n                    last_presentation_time - now,\n                );\n                now = last_presentation_time + Duration::from_nanos(refresh_interval_ns);\n            }\n        }\n\n        let since_last = now - last_presentation_time;\n        let since_last_ns =\n            since_last.as_secs() * 1_000_000_000 + u64::from(since_last.subsec_nanos());\n        let to_next_ns = (since_last_ns / refresh_interval_ns + 1) * refresh_interval_ns;\n\n        // If VRR is enabled and more than one frame passed since last presentation, assume that we\n        // can present immediately.\n        if self.vrr && to_next_ns > refresh_interval_ns {\n            now\n        } else {\n            last_presentation_time + Duration::from_nanos(to_next_ns)\n        }\n    }\n}\n"
  },
  {
    "path": "src/handlers/compositor.rs",
    "content": "use std::collections::hash_map::Entry;\n\nuse niri_ipc::PositionChange;\nuse smithay::backend::renderer::utils::on_commit_buffer_handler;\nuse smithay::input::pointer::{CursorImageStatus, CursorImageSurfaceData};\nuse smithay::reexports::calloop::Interest;\nuse smithay::reexports::wayland_server::protocol::wl_buffer;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::reexports::wayland_server::{Client, Resource};\nuse smithay::wayland::buffer::BufferHandler;\nuse smithay::wayland::compositor::{\n    add_blocker, add_pre_commit_hook, get_parent, is_sync_subsurface, remove_pre_commit_hook,\n    with_states, BufferAssignment, CompositorClientState, CompositorHandler, CompositorState,\n    SurfaceAttributes,\n};\nuse smithay::wayland::dmabuf::get_dmabuf;\nuse smithay::wayland::shell::xdg::ToplevelCachedState;\nuse smithay::wayland::shm::{ShmHandler, ShmState};\nuse smithay::{delegate_compositor, delegate_shm};\n\nuse super::xdg_shell::add_mapped_toplevel_pre_commit_hook;\nuse crate::handlers::XDG_ACTIVATION_TOKEN_TIMEOUT;\nuse crate::layout::{ActivateWindow, AddWindowTarget, LayoutElement as _};\nuse crate::niri::{CastTarget, ClientState, LockState, State};\nuse crate::utils::transaction::Transaction;\nuse crate::utils::{is_mapped, send_scale_transform};\nuse crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped};\n\nimpl CompositorHandler for State {\n    fn compositor_state(&mut self) -> &mut CompositorState {\n        &mut self.niri.compositor_state\n    }\n\n    fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {\n        &client.get_data::<ClientState>().unwrap().compositor_state\n    }\n\n    fn new_subsurface(&mut self, surface: &WlSurface, parent: &WlSurface) {\n        let mut root = parent.clone();\n        while let Some(parent) = get_parent(&root) {\n            root = parent;\n        }\n\n        if let Some(output) = self.niri.output_for_root(&root) {\n            let scale = output.current_scale();\n            let transform = output.current_transform();\n            with_states(surface, |data| {\n                send_scale_transform(surface, data, scale, transform);\n            });\n        }\n    }\n\n    fn new_surface(&mut self, surface: &WlSurface) {\n        self.add_default_dmabuf_pre_commit_hook(surface);\n    }\n\n    fn commit(&mut self, surface: &WlSurface) {\n        let _span = tracy_client::span!(\"CompositorHandler::commit\");\n        let _span = trace_span!(\"commit\", surface = %surface.id()).entered();\n        trace!(\"commit\");\n\n        on_commit_buffer_handler::<Self>(surface);\n        self.backend.early_import(surface);\n\n        let mut root_surface = surface.clone();\n        while let Some(parent) = get_parent(&root_surface) {\n            root_surface = parent;\n        }\n\n        // Update the cached root surface.\n        self.niri\n            .root_surface\n            .insert(surface.clone(), root_surface.clone());\n\n        if is_sync_subsurface(surface) {\n            return;\n        }\n\n        if surface == &root_surface {\n            // This is a root surface commit. It might have mapped a previously-unmapped toplevel.\n            if let Entry::Occupied(entry) = self.niri.unmapped_windows.entry(surface.clone()) {\n                if is_mapped(surface) {\n                    // The toplevel got mapped.\n                    let Unmapped {\n                        window,\n                        state,\n                        activation_token_data,\n                    } = entry.remove();\n\n                    window.on_commit();\n\n                    let toplevel = window.toplevel().expect(\"no X11 support\");\n\n                    let (\n                        rules,\n                        width,\n                        height,\n                        is_full_width,\n                        output,\n                        workspace_id,\n                        is_pending_maximized,\n                    ) = if let InitialConfigureState::Configured {\n                        rules,\n                        width,\n                        height,\n                        floating_width: _,\n                        floating_height: _,\n                        is_full_width,\n                        output,\n                        workspace_name,\n                        is_pending_maximized,\n                    } = state\n                    {\n                        // Check that the output is still connected.\n                        let output =\n                            output.filter(|o| self.niri.layout.monitor_for_output(o).is_some());\n\n                        // Check that the workspace still exists.\n                        let workspace_id = workspace_name\n                            .as_deref()\n                            .and_then(|n| self.niri.layout.find_workspace_by_name(n))\n                            .map(|(_, ws)| ws.id());\n\n                        (\n                            rules,\n                            width,\n                            height,\n                            is_full_width,\n                            output,\n                            workspace_id,\n                            is_pending_maximized,\n                        )\n                    } else {\n                        // Can happen when a surface unmaps by attaching a null buffer while\n                        // there are in-flight pending configures.\n                        debug!(\"window mapped without proper initial configure\");\n                        (\n                            ResolvedWindowRules::default(),\n                            None,\n                            None,\n                            false,\n                            None,\n                            None,\n                            false,\n                        )\n                    };\n\n                    // The GTK about dialog sets min/max size after the initial configure but\n                    // before mapping, so we need to compute open_floating at the last possible\n                    // moment, that is here.\n                    let is_floating = rules.compute_open_floating(toplevel);\n\n                    // Figure out if we should activate the window.\n                    let activate = rules.open_focused.map(|focus| {\n                        if focus {\n                            ActivateWindow::Yes\n                        } else {\n                            ActivateWindow::No\n                        }\n                    });\n                    let activate = activate.unwrap_or_else(|| {\n                        // Check the token timestamp again in case the window took a while between\n                        // requesting activation and mapping.\n                        let token = activation_token_data.filter(|token| {\n                            token.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT\n                        });\n                        if token.is_some() {\n                            ActivateWindow::Yes\n                        } else {\n                            let config = self.niri.config.borrow();\n                            if config.debug.strict_new_window_focus_policy {\n                                ActivateWindow::No\n                            } else {\n                                ActivateWindow::Smart\n                            }\n                        }\n                    });\n\n                    let parent = toplevel\n                        .parent()\n                        .and_then(|parent| self.niri.layout.find_window_and_output(&parent))\n                        // Only consider the parent if we configured the window for the same\n                        // output.\n                        //\n                        // Normally when we're following the parent, the configured output will be\n                        // None. If the configured output is set, that means it was set explicitly\n                        // by a window rule or a fullscreen request.\n                        .filter(|(_, parent_output)| {\n                            parent_output.is_none()\n                                || output.is_none()\n                                || output.as_ref() == *parent_output\n                        })\n                        .map(|(mapped, _)| mapped.window.clone());\n\n                    // The mapped pre-commit hook deals with dma-bufs on its own.\n                    self.remove_default_dmabuf_pre_commit_hook(surface);\n                    let hook = add_mapped_toplevel_pre_commit_hook(toplevel);\n                    let mapped = Mapped::new(window, rules, hook);\n                    let window = mapped.window.clone();\n\n                    let target = if let Some(p) = &parent {\n                        // Open dialogs next to their parent window.\n                        AddWindowTarget::NextTo(p)\n                    } else if let Some(id) = workspace_id {\n                        AddWindowTarget::Workspace(id)\n                    } else if let Some(output) = &output {\n                        AddWindowTarget::Output(output)\n                    } else {\n                        AddWindowTarget::Auto\n                    };\n                    let output = self.niri.layout.add_window(\n                        mapped,\n                        target,\n                        width,\n                        height,\n                        is_full_width,\n                        is_floating,\n                        activate,\n                    );\n                    let output = output.cloned();\n\n                    // The window state cannot contain Fullscreen and Maximized at once. Therefore,\n                    // if the window ended up fullscreen, then we only know that it is also\n                    // maximized from the is_pending_maximized variable. Tell the layout about it\n                    // here so that unfullscreening the window makes it maximized.\n                    if let Some((mapped, _)) = self.niri.layout.find_window_and_output(surface) {\n                        if mapped.pending_sizing_mode().is_fullscreen() && is_pending_maximized {\n                            self.niri.layout.set_maximized(&window, true);\n                        }\n                    } else {\n                        error!(\"layout is missing the window that we just added\");\n                    }\n\n                    if let Some(output) = output {\n                        self.niri.layout.start_open_animation_for_window(&window);\n\n                        let new_focus = self.niri.layout.focus().map(|m| &m.window);\n                        if new_focus == Some(&window) {\n                            // We activated the newly opened window.\n                            self.maybe_warp_cursor_to_focus();\n                            self.niri.layer_shell_on_demand_focus = None;\n                        }\n\n                        self.niri.queue_redraw(&output);\n                    }\n                    return;\n                }\n\n                // The toplevel remains unmapped.\n                trace!(\"toplevel remains unmapped\");\n                let unmapped = entry.get();\n                if unmapped.needs_initial_configure() {\n                    let toplevel = unmapped.window.toplevel().expect(\"no x11 support\").clone();\n                    self.queue_initial_configure(toplevel);\n                }\n                return;\n            }\n\n            // This is a commit of a previously-mapped root or a non-toplevel root.\n            if let Some((mapped, output)) = self.niri.layout.find_window_and_output(surface) {\n                let window = mapped.window.clone();\n                let output = output.cloned();\n\n                let id = mapped.id();\n\n                // This is a commit of a previously-mapped toplevel.\n                let is_mapped = is_mapped(surface);\n\n                // Must start the close animation before window.on_commit().\n                let transaction = Transaction::new();\n                if !is_mapped {\n                    let blocker = transaction.blocker();\n                    self.backend.with_primary_renderer(|renderer| {\n                        self.niri\n                            .layout\n                            .start_close_animation_for_window(renderer, &window, blocker);\n                    });\n                }\n\n                window.on_commit();\n\n                if !is_mapped {\n                    // The toplevel got unmapped.\n                    //\n                    // Test client: wleird-unmap.\n                    trace!(\"toplevel got unmapped\");\n\n                    let active_window = self.niri.layout.focus().map(|m| &m.window);\n                    let was_active = active_window == Some(&window);\n\n                    self.niri\n                        .stop_casts_for_target(CastTarget::Window { id: id.get() });\n\n                    self.niri.window_mru_ui.remove_window(id);\n                    self.niri.layout.remove_window(&window, transaction.clone());\n                    self.add_default_dmabuf_pre_commit_hook(surface);\n\n                    // If this is the only instance, then this transaction will complete\n                    // immediately, so no need to set the timer.\n                    if !transaction.is_last() {\n                        transaction.register_deadline_timer(&self.niri.event_loop);\n                    }\n\n                    if was_active {\n                        self.maybe_warp_cursor_to_focus();\n                    }\n\n                    // Newly-unmapped toplevels must perform the initial commit-configure sequence\n                    // afresh.\n                    let unmapped = Unmapped::new(window);\n                    self.niri.unmapped_windows.insert(surface.clone(), unmapped);\n\n                    if let Some(output) = output {\n                        self.niri.queue_redraw(&output);\n                        self.niri.queue_redraw_mru_output();\n                    }\n                    return;\n                }\n\n                let (serial, buffer_delta) = with_states(surface, |states| {\n                    let buffer_delta = states\n                        .cached_state\n                        .get::<SurfaceAttributes>()\n                        .current()\n                        .buffer_delta\n                        .take();\n\n                    let serial = states\n                        .cached_state\n                        .get::<ToplevelCachedState>()\n                        .current()\n                        .last_acked\n                        .as_ref()\n                        .map(|c| c.serial);\n                    (serial, buffer_delta)\n                });\n                if serial.is_none() {\n                    error!(\"commit on a mapped surface without a configured serial\");\n                }\n\n                // The toplevel remains mapped.\n                self.niri.window_mru_ui.update_window(&self.niri.layout, id);\n                self.niri.layout.update_window(&window, serial);\n\n                // Move the toplevel according to the attach offset.\n                if let Some(delta) = buffer_delta {\n                    if delta.x != 0 || delta.y != 0 {\n                        let (x, y) = delta.to_f64().into();\n                        self.niri.layout.move_floating_window(\n                            Some(&window),\n                            PositionChange::AdjustFixed(x),\n                            PositionChange::AdjustFixed(y),\n                            false,\n                        );\n                    }\n                }\n\n                // Popup placement depends on window size which might have changed.\n                self.update_reactive_popups(&window);\n\n                if let Some(output) = output {\n                    self.niri.queue_redraw(&output);\n                    self.niri.queue_redraw_mru_output();\n                }\n                return;\n            }\n\n            // This is a commit of a non-toplevel root.\n        }\n\n        // This is a commit of a non-root or a non-toplevel root.\n        let root_window_output = self.niri.layout.find_window_and_output(&root_surface);\n        if let Some((mapped, output)) = root_window_output {\n            let window = mapped.window.clone();\n            let output = output.cloned();\n            window.on_commit();\n            self.niri\n                .window_mru_ui\n                .update_window(&self.niri.layout, mapped.id());\n            self.niri.layout.update_window(&window, None);\n            if let Some(output) = output {\n                self.niri.queue_redraw(&output);\n                self.niri.queue_redraw_mru_output();\n            }\n            return;\n        }\n\n        // This might be a popup.\n        self.popups_handle_commit(surface);\n        if let Some(popup) = self.niri.popups.find_popup(surface) {\n            if let Some(output) = self.output_for_popup(&popup) {\n                self.niri.queue_redraw(&output.clone());\n            }\n            return;\n        }\n\n        // This might be a layer-shell surface.\n        if self.layer_shell_handle_commit(surface) {\n            return;\n        }\n\n        // This might be a cursor surface.\n        if matches!(\n            &self.niri.cursor_manager.cursor_image(),\n            CursorImageStatus::Surface(s) if s == &root_surface\n        ) {\n            // In case the cursor surface has been committed handle the role specific\n            // buffer offset by applying the offset on the cursor image hotspot\n            if surface == &root_surface {\n                with_states(surface, |states| {\n                    let cursor_image_attributes = states.data_map.get::<CursorImageSurfaceData>();\n\n                    if let Some(mut cursor_image_attributes) =\n                        cursor_image_attributes.map(|attrs| attrs.lock().unwrap())\n                    {\n                        let buffer_delta = states\n                            .cached_state\n                            .get::<SurfaceAttributes>()\n                            .current()\n                            .buffer_delta\n                            .take();\n                        if let Some(buffer_delta) = buffer_delta {\n                            cursor_image_attributes.hotspot -= buffer_delta;\n                        }\n                    }\n                });\n            }\n\n            // FIXME: granular redraws for cursors.\n            self.niri.queue_redraw_all();\n            return;\n        }\n\n        // This might be a DnD icon surface.\n        if matches!(&self.niri.dnd_icon, Some(icon) if icon.surface == root_surface) {\n            let dnd_icon = self.niri.dnd_icon.as_mut().unwrap();\n\n            // In case the dnd surface has been committed handle the role specific\n            // buffer offset by applying the offset on the dnd icon offset\n            if surface == &dnd_icon.surface {\n                with_states(&dnd_icon.surface, |states| {\n                    let buffer_delta = states\n                        .cached_state\n                        .get::<SurfaceAttributes>()\n                        .current()\n                        .buffer_delta\n                        .take()\n                        .unwrap_or_default();\n                    dnd_icon.offset += buffer_delta;\n                });\n            }\n\n            // FIXME: granular redraws for cursors.\n            self.niri.queue_redraw_all();\n            return;\n        }\n\n        // This might be a lock surface.\n        for (output, state) in &self.niri.output_state {\n            if let Some(lock_surface) = &state.lock_surface {\n                if lock_surface.wl_surface() == &root_surface {\n                    if matches!(self.niri.lock_state, LockState::WaitingForSurfaces { .. }) {\n                        self.niri.maybe_continue_to_locking();\n                    } else {\n                        self.niri.queue_redraw(&output.clone());\n                    }\n\n                    return;\n                }\n            }\n        }\n\n        // This message can trigger for lock surfaces that had a commit right after we unlocked\n        // the session, but that's ok, we don't need to handle them.\n        trace!(\"commit on an unrecognized surface: {surface:?}, root: {root_surface:?}\");\n    }\n\n    fn destroyed(&mut self, surface: &WlSurface) {\n        // Clients may destroy their subsurfaces before the main surface. Ensure we have a snapshot\n        // when that happens, so that the closing animation includes all these subsurfaces.\n        //\n        // Test client: alacritty with CSD <= 0.13 (it was fixed in winit afterwards:\n        // https://github.com/rust-windowing/winit/pull/3625).\n        //\n        // This is still not perfect, as this function is called already after the (first)\n        // subsurface is destroyed; in the case of alacritty, this is the top CSD shadow. But, it\n        // gets most of the job done.\n        if let Some(root) = self.niri.root_surface.get(surface) {\n            if let Some((mapped, _)) = self.niri.layout.find_window_and_output(root) {\n                let window = mapped.window.clone();\n                self.backend.with_primary_renderer(|renderer| {\n                    self.niri.layout.store_unmap_snapshot(renderer, &window);\n                });\n            }\n        }\n\n        self.niri\n            .root_surface\n            .retain(|k, v| k != surface && v != surface);\n\n        // The object destruction order is not guaranteed to follow the logical role order. So for\n        // example when a client disconnects unexpectedly, WlSurface::destroyed() may be called\n        // before XdgShellHandler::toplevel_destroyed(). In this case, the surface will *not* have\n        // the default dmabuf pre-commit hook: it will still have the toplevel pre-commit hook.\n        //\n        // So, this may come out empty, and then the toplevel pre-commit hook will be removed in the\n        // subsequent toplevel_destroyed() call.\n        if let Some(hook) = self.niri.dmabuf_pre_commit_hook.remove(surface) {\n            remove_pre_commit_hook(surface, hook);\n        }\n    }\n}\n\nimpl BufferHandler for State {\n    fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}\n}\n\nimpl ShmHandler for State {\n    fn shm_state(&self) -> &ShmState {\n        &self.niri.shm_state\n    }\n}\n\ndelegate_compositor!(State);\ndelegate_shm!(State);\n\nimpl State {\n    pub fn add_default_dmabuf_pre_commit_hook(&mut self, surface: &WlSurface) {\n        if !surface.is_alive() {\n            error!(\"tried to add dmabuf pre-commit hook for a dead surface\");\n            return;\n        }\n\n        let hook = add_pre_commit_hook::<Self, _>(surface, move |state, _dh, surface| {\n            let maybe_dmabuf = with_states(surface, |surface_data| {\n                surface_data\n                    .cached_state\n                    .get::<SurfaceAttributes>()\n                    .pending()\n                    .buffer\n                    .as_ref()\n                    .and_then(|assignment| match assignment {\n                        BufferAssignment::NewBuffer(buffer) => get_dmabuf(buffer).cloned().ok(),\n                        _ => None,\n                    })\n            });\n            if let Some(dmabuf) = maybe_dmabuf {\n                if let Ok((blocker, source)) = dmabuf.generate_blocker(Interest::READ) {\n                    if let Some(client) = surface.client() {\n                        let res =\n                            state\n                                .niri\n                                .event_loop\n                                .insert_source(source, move |_, _, state| {\n                                    let display_handle = state.niri.display_handle.clone();\n                                    state\n                                        .client_compositor_state(&client)\n                                        .blocker_cleared(state, &display_handle);\n                                    Ok(())\n                                });\n                        if res.is_ok() {\n                            add_blocker(surface, blocker);\n                            trace!(\"added default dmabuf blocker\");\n                        }\n                    }\n                }\n            }\n        });\n\n        let s = surface.clone();\n        if let Some(prev) = self.niri.dmabuf_pre_commit_hook.insert(s, hook) {\n            error!(\"tried to add dmabuf pre-commit hook when there was already one\");\n            remove_pre_commit_hook(surface, prev);\n        }\n    }\n\n    pub fn remove_default_dmabuf_pre_commit_hook(&mut self, surface: &WlSurface) {\n        if let Some(hook) = self.niri.dmabuf_pre_commit_hook.remove(surface) {\n            remove_pre_commit_hook(surface, hook);\n        } else {\n            error!(\"tried to remove dmabuf pre-commit hook but there was none\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/handlers/layer_shell.rs",
    "content": "use smithay::delegate_layer_shell;\nuse smithay::desktop::{layer_map_for_output, LayerSurface, PopupKind, WindowSurfaceType};\nuse smithay::output::Output;\nuse smithay::reexports::wayland_server::protocol::wl_output::WlOutput;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::wayland::compositor::{get_parent, with_states};\nuse smithay::wayland::shell::wlr_layer::{\n    self, Layer, LayerSurface as WlrLayerSurface, LayerSurfaceData, WlrLayerShellHandler,\n    WlrLayerShellState,\n};\nuse smithay::wayland::shell::xdg::PopupSurface;\n\nuse crate::layer::{MappedLayer, ResolvedLayerRules};\nuse crate::niri::State;\nuse crate::utils::{is_mapped, output_size, send_scale_transform};\n\nimpl WlrLayerShellHandler for State {\n    fn shell_state(&mut self) -> &mut WlrLayerShellState {\n        &mut self.niri.layer_shell_state\n    }\n\n    fn new_layer_surface(\n        &mut self,\n        surface: WlrLayerSurface,\n        wl_output: Option<WlOutput>,\n        _layer: Layer,\n        namespace: String,\n    ) {\n        let output = if let Some(wl_output) = &wl_output {\n            Output::from_resource(wl_output)\n        } else {\n            self.niri.layout.active_output().cloned()\n        };\n        let Some(output) = output else {\n            warn!(\"no output for new layer surface, closing\");\n            surface.send_close();\n            return;\n        };\n\n        let wl_surface = surface.wl_surface().clone();\n        let is_new = self.niri.unmapped_layer_surfaces.insert(wl_surface);\n        assert!(is_new);\n\n        let mut map = layer_map_for_output(&output);\n        map.map_layer(&LayerSurface::new(surface, namespace))\n            .unwrap();\n    }\n\n    fn layer_destroyed(&mut self, surface: WlrLayerSurface) {\n        let wl_surface = surface.wl_surface();\n        self.niri.unmapped_layer_surfaces.remove(wl_surface);\n\n        let output = if let Some((output, mut map, layer)) =\n            self.niri.layout.outputs().find_map(|o| {\n                let map = layer_map_for_output(o);\n                let layer = map\n                    .layers()\n                    .find(|&layer| layer.layer_surface() == &surface)\n                    .cloned();\n                layer.map(|layer| (o.clone(), map, layer))\n            }) {\n            map.unmap_layer(&layer);\n            self.niri.mapped_layer_surfaces.remove(&layer);\n            Some(output)\n        } else {\n            None\n        };\n        if let Some(output) = output {\n            self.niri.output_resized(&output);\n        }\n    }\n\n    fn new_popup(&mut self, _parent: WlrLayerSurface, popup: PopupSurface) {\n        self.unconstrain_popup(&PopupKind::Xdg(popup));\n    }\n}\ndelegate_layer_shell!(State);\n\nimpl State {\n    pub fn layer_shell_handle_commit(&mut self, surface: &WlSurface) -> bool {\n        let mut root_surface = surface.clone();\n        while let Some(parent) = get_parent(&root_surface) {\n            root_surface = parent;\n        }\n\n        let output = self\n            .niri\n            .layout\n            .outputs()\n            .find(|o| {\n                let map = layer_map_for_output(o);\n                map.layer_for_surface(&root_surface, WindowSurfaceType::TOPLEVEL)\n                    .is_some()\n            })\n            .cloned();\n        let Some(output) = output else {\n            return false;\n        };\n\n        if surface != &root_surface {\n            // This is an unsync layer-shell subsurface.\n            self.niri.queue_redraw(&output);\n            return true;\n        }\n\n        let mut map = layer_map_for_output(&output);\n\n        // Arrange the layers before sending the initial configure to respect any size the\n        // client may have sent.\n        map.arrange();\n\n        let layer = map\n            .layer_for_surface(surface, WindowSurfaceType::TOPLEVEL)\n            .unwrap();\n\n        if is_mapped(surface) {\n            let was_unmapped = self.niri.unmapped_layer_surfaces.remove(surface);\n\n            // Resolve rules for newly mapped layer surfaces.\n            if was_unmapped {\n                let config = self.niri.config.borrow();\n\n                let rules = &config.layer_rules;\n                let rules = ResolvedLayerRules::compute(rules, layer, self.niri.is_at_startup);\n\n                let output_size = output_size(&output);\n                let scale = output.current_scale().fractional_scale();\n\n                let mapped = MappedLayer::new(\n                    layer.clone(),\n                    rules,\n                    output_size,\n                    scale,\n                    self.niri.clock.clone(),\n                    &config,\n                );\n\n                let prev = self\n                    .niri\n                    .mapped_layer_surfaces\n                    .insert(layer.clone(), mapped);\n                if prev.is_some() {\n                    error!(\"MappedLayer was present for an unmapped surface\");\n                }\n            }\n\n            // Give focus to newly mapped on-demand surfaces. Some launchers like lxqt-runner rely\n            // on this behavior. While this behavior doesn't make much sense for other clients like\n            // panels, the consensus seems to be that it's not a big deal since panels generally\n            // only open once at the start of the session.\n            //\n            // Note that:\n            // 1) Exclusive layer surfaces already get focus automatically in\n            //    update_keyboard_focus().\n            // 2) Same-layer exclusive layer surfaces are already preferred to on-demand surfaces in\n            //    update_keyboard_focus(), so we don't need to check for that here.\n            //\n            // https://github.com/niri-wm/niri/issues/641\n            let on_demand = layer.cached_state().keyboard_interactivity\n                == wlr_layer::KeyboardInteractivity::OnDemand;\n            if was_unmapped && on_demand {\n                // I guess it'd make sense to check that no higher-layer on-demand surface\n                // has focus, but Smithay's Layer doesn't implement Ord so this would be a\n                // little annoying.\n                self.niri.layer_shell_on_demand_focus = Some(layer.clone());\n            }\n        } else {\n            // The surface is unmapped.\n            if self.niri.mapped_layer_surfaces.remove(layer).is_some() {\n                // A mapped surface got unmapped via a null commit. Now it needs to do a new\n                // initial commit again.\n                self.niri.unmapped_layer_surfaces.insert(surface.clone());\n            } else {\n                // An unmapped surface remains unmapped. If we haven't sent an initial configure\n                // yet, we should do so.\n                let initial_configure_sent = with_states(surface, |states| {\n                    states\n                        .data_map\n                        .get::<LayerSurfaceData>()\n                        .unwrap()\n                        .lock()\n                        .unwrap()\n                        .initial_configure_sent\n                });\n                if !initial_configure_sent {\n                    let scale = output.current_scale();\n                    let transform = output.current_transform();\n                    with_states(surface, |data| {\n                        send_scale_transform(surface, data, scale, transform);\n                    });\n\n                    layer.layer_surface().send_configure();\n                }\n                // If we already sent an initial configure, then map.arrange() above had just sent\n                // it a new configure, if needed.\n            }\n        }\n\n        drop(map);\n\n        // This will call queue_redraw() inside.\n        self.niri.output_resized(&output);\n\n        true\n    }\n}\n"
  },
  {
    "path": "src/handlers/mod.rs",
    "content": "mod compositor;\nmod layer_shell;\nmod xdg_shell;\n\nuse std::fs::File;\nuse std::io::Write;\nuse std::os::fd::OwnedFd;\nuse std::sync::Arc;\nuse std::thread;\nuse std::time::Duration;\n\nuse smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::drm::DrmNode;\nuse smithay::backend::input::{InputEvent, TabletToolDescriptor};\nuse smithay::desktop::{PopupKind, PopupManager};\nuse smithay::input::dnd::{self, DnDGrab, DndGrabHandler, DndTarget};\nuse smithay::input::pointer::{CursorIcon, CursorImageStatus, Focus, PointerHandle};\nuse smithay::input::{keyboard, Seat, SeatHandler, SeatState};\nuse smithay::output::Output;\nuse smithay::reexports::rustix::fs::{fcntl_setfl, OFlags};\nuse smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;\nuse smithay::reexports::wayland_server::protocol::wl_output::WlOutput;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::reexports::wayland_server::Resource;\nuse smithay::utils::{Logical, Point, Rectangle, Serial};\nuse smithay::wayland::compositor::{get_parent, with_states};\nuse smithay::wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier};\nuse smithay::wayland::drm_lease::{\n    DrmLease, DrmLeaseBuilder, DrmLeaseHandler, DrmLeaseRequest, DrmLeaseState, LeaseRejected,\n};\nuse smithay::wayland::fractional_scale::FractionalScaleHandler;\nuse smithay::wayland::idle_inhibit::IdleInhibitHandler;\nuse smithay::wayland::idle_notify::{IdleNotifierHandler, IdleNotifierState};\nuse smithay::wayland::input_method::{InputMethodHandler, PopupSurface};\nuse smithay::wayland::keyboard_shortcuts_inhibit::{\n    KeyboardShortcutsInhibitHandler, KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor,\n};\nuse smithay::wayland::output::OutputHandler;\nuse smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsHandler};\nuse smithay::wayland::security_context::{\n    SecurityContext, SecurityContextHandler, SecurityContextListenerSource,\n};\nuse smithay::wayland::selection::data_device::{\n    set_data_device_focus, DataDeviceHandler, DataDeviceState, WaylandDndGrabHandler,\n};\nuse smithay::wayland::selection::ext_data_control::{\n    DataControlHandler as ExtDataControlHandler, DataControlState as ExtDataControlState,\n};\nuse smithay::wayland::selection::primary_selection::{\n    set_primary_focus, PrimarySelectionHandler, PrimarySelectionState,\n};\nuse smithay::wayland::selection::wlr_data_control::{\n    DataControlHandler as WlrDataControlHandler, DataControlState as WlrDataControlState,\n};\nuse smithay::wayland::selection::{SelectionHandler, SelectionTarget};\nuse smithay::wayland::session_lock::{\n    LockSurface, SessionLockHandler, SessionLockManagerState, SessionLocker,\n};\nuse smithay::wayland::tablet_manager::TabletSeatHandler;\nuse smithay::wayland::xdg_activation::{\n    XdgActivationHandler, XdgActivationState, XdgActivationToken, XdgActivationTokenData,\n};\nuse smithay::{\n    delegate_cursor_shape, delegate_data_control, delegate_data_device, delegate_dmabuf,\n    delegate_drm_lease, delegate_ext_data_control, delegate_fractional_scale,\n    delegate_idle_inhibit, delegate_idle_notify, delegate_input_method_manager,\n    delegate_keyboard_shortcuts_inhibit, delegate_output, delegate_pointer_constraints,\n    delegate_pointer_gestures, delegate_presentation, delegate_primary_selection,\n    delegate_relative_pointer, delegate_seat, delegate_security_context, delegate_session_lock,\n    delegate_single_pixel_buffer, delegate_tablet_manager, delegate_text_input_manager,\n    delegate_viewporter, delegate_virtual_keyboard_manager, delegate_xdg_activation,\n};\n\npub use crate::handlers::xdg_shell::KdeDecorationsModeState;\nuse crate::layout::workspace::WorkspaceId;\nuse crate::layout::ActivateWindow;\nuse crate::niri::{DndIcon, NewClient, State};\nuse crate::protocols::ext_workspace::{self, ExtWorkspaceHandler, ExtWorkspaceManagerState};\nuse crate::protocols::foreign_toplevel::{\n    self, ForeignToplevelHandler, ForeignToplevelManagerState,\n};\nuse crate::protocols::gamma_control::{GammaControlHandler, GammaControlManagerState};\nuse crate::protocols::mutter_x11_interop::MutterX11InteropHandler;\nuse crate::protocols::output_management::{OutputManagementHandler, OutputManagementManagerState};\nuse crate::protocols::screencopy::{Screencopy, ScreencopyHandler, ScreencopyManagerState};\nuse crate::protocols::virtual_pointer::{\n    VirtualPointerAxisEvent, VirtualPointerButtonEvent, VirtualPointerHandler,\n    VirtualPointerInputBackend, VirtualPointerManagerState, VirtualPointerMotionAbsoluteEvent,\n    VirtualPointerMotionEvent,\n};\nuse crate::utils::{output_size, send_scale_transform};\nuse crate::{\n    delegate_ext_workspace, delegate_foreign_toplevel, delegate_gamma_control,\n    delegate_mutter_x11_interop, delegate_output_management, delegate_screencopy,\n    delegate_virtual_pointer,\n};\n\npub const XDG_ACTIVATION_TOKEN_TIMEOUT: Duration = Duration::from_secs(10);\n\nimpl SeatHandler for State {\n    type KeyboardFocus = WlSurface;\n    type PointerFocus = WlSurface;\n    type TouchFocus = WlSurface;\n\n    fn seat_state(&mut self) -> &mut SeatState<State> {\n        &mut self.niri.seat_state\n    }\n\n    fn cursor_image(&mut self, _seat: &Seat<Self>, mut image: CursorImageStatus) {\n        // FIXME: this hack should be removable once the screenshot UI is tracked with a\n        // PointerFocus properly.\n        if self.niri.screenshot_ui.is_open() {\n            image = CursorImageStatus::Named(CursorIcon::Crosshair);\n        }\n        self.niri.cursor_manager.set_cursor_image(image);\n        // FIXME: more granular\n        self.niri.queue_redraw_all();\n    }\n\n    fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) {\n        let dh = &self.niri.display_handle;\n        let client = focused.and_then(|s| dh.get_client(s.id()).ok());\n        set_data_device_focus(dh, seat, client.clone());\n        set_primary_focus(dh, seat, client);\n    }\n\n    fn led_state_changed(&mut self, _seat: &Seat<Self>, led_state: keyboard::LedState) {\n        let keyboards = self\n            .niri\n            .devices\n            .iter()\n            .filter(|device| device.has_capability(input::DeviceCapability::Keyboard))\n            .cloned();\n\n        for mut keyboard in keyboards {\n            keyboard.led_update(led_state.into());\n        }\n    }\n}\ndelegate_seat!(State);\ndelegate_cursor_shape!(State);\ndelegate_pointer_gestures!(State);\ndelegate_relative_pointer!(State);\ndelegate_text_input_manager!(State);\n\nimpl TabletSeatHandler for State {\n    fn tablet_tool_image(&mut self, _tool: &TabletToolDescriptor, image: CursorImageStatus) {\n        // FIXME: tablet tools should have their own cursors.\n        self.niri.cursor_manager.set_cursor_image(image);\n        // FIXME: granular.\n        self.niri.queue_redraw_all();\n    }\n}\ndelegate_tablet_manager!(State);\n\nimpl PointerConstraintsHandler for State {\n    fn new_constraint(&mut self, _surface: &WlSurface, _pointer: &PointerHandle<Self>) {\n        // Pointer constraints track pointer focus internally, so make sure it's up to date before\n        // activating a new one.\n        self.refresh_pointer_contents();\n\n        self.niri.maybe_activate_pointer_constraint();\n    }\n\n    fn cursor_position_hint(\n        &mut self,\n        surface: &WlSurface,\n        pointer: &PointerHandle<Self>,\n        location: Point<f64, Logical>,\n    ) {\n        let is_constraint_active = with_pointer_constraint(surface, pointer, |constraint| {\n            constraint.is_some_and(|c| c.is_active())\n        });\n\n        if !is_constraint_active {\n            return;\n        }\n\n        // Note: this is surface under pointer, not pointer focus. So if you start, say, a\n        // middle-drag in Blender, then touchpad-swipe the window away, the surface under pointer\n        // will change, even though the real pointer focus remains on the Blender surface due to\n        // the click grab.\n        //\n        // Ideally we would just use the constraint surface, but we need its origin. So this is\n        // more of a hack because pointer contents has the surface origin available.\n        //\n        // FIXME: use the constraint surface somehow, don't use pointer contents.\n        let Some((ref surface_under_pointer, origin)) = self.niri.pointer_contents.surface else {\n            return;\n        };\n\n        if surface_under_pointer != surface {\n            return;\n        }\n\n        let mut root = surface.clone();\n        while let Some(parent) = get_parent(&root) {\n            root = parent;\n        }\n\n        let target = self\n            .niri\n            .output_for_root(&root)\n            .and_then(|output| self.niri.global_space.output_geometry(output))\n            .map_or(origin + location, |mut output_geometry| {\n                // i32 sizes are exclusive, but f64 sizes are inclusive.\n                output_geometry.size -= (1, 1).into();\n                (origin + location).constrain(output_geometry.to_f64())\n            });\n        pointer.set_location(target);\n\n        // Redraw to update the cursor position if it's visible.\n        if self.niri.pointer_visibility.is_visible() {\n            // FIXME: redraw only outputs overlapping the cursor.\n            self.niri.queue_redraw_all();\n        }\n    }\n}\ndelegate_pointer_constraints!(State);\n\nimpl InputMethodHandler for State {\n    fn new_popup(&mut self, surface: PopupSurface) {\n        let popup = PopupKind::InputMethod(surface);\n        if let Some(output) = self.output_for_popup(&popup) {\n            let scale = output.current_scale();\n            let transform = output.current_transform();\n            let wl_surface = popup.wl_surface();\n            with_states(wl_surface, |data| {\n                send_scale_transform(wl_surface, data, scale, transform);\n            });\n        }\n\n        self.unconstrain_popup(&popup);\n\n        if let Err(err) = self.niri.popups.track_popup(popup) {\n            warn!(\"error tracking ime popup {err:?}\");\n        }\n    }\n\n    fn popup_repositioned(&mut self, surface: PopupSurface) {\n        let popup = PopupKind::InputMethod(surface);\n        self.unconstrain_popup(&popup);\n    }\n\n    fn dismiss_popup(&mut self, surface: PopupSurface) {\n        if let Some(parent) = surface.get_parent().map(|parent| parent.surface.clone()) {\n            let _ = PopupManager::dismiss_popup(&parent, &PopupKind::from(surface));\n        }\n    }\n\n    fn parent_geometry(&self, parent: &WlSurface) -> Rectangle<i32, Logical> {\n        self.niri\n            .layout\n            .find_window_and_output(parent)\n            .map(|(mapped, _)| mapped.window.geometry())\n            .unwrap_or_default()\n    }\n}\n\nimpl KeyboardShortcutsInhibitHandler for State {\n    fn keyboard_shortcuts_inhibit_state(&mut self) -> &mut KeyboardShortcutsInhibitState {\n        &mut self.niri.keyboard_shortcuts_inhibit_state\n    }\n\n    fn new_inhibitor(&mut self, inhibitor: KeyboardShortcutsInhibitor) {\n        // FIXME: show a confirmation dialog with a \"remember for this application\" kind of toggle.\n        inhibitor.activate();\n        self.niri\n            .keyboard_shortcuts_inhibiting_surfaces\n            .insert(inhibitor.wl_surface().clone(), inhibitor);\n    }\n\n    fn inhibitor_destroyed(&mut self, inhibitor: KeyboardShortcutsInhibitor) {\n        self.niri\n            .keyboard_shortcuts_inhibiting_surfaces\n            .remove(&inhibitor.wl_surface().clone());\n    }\n}\n\ndelegate_input_method_manager!(State);\ndelegate_keyboard_shortcuts_inhibit!(State);\ndelegate_virtual_keyboard_manager!(State);\n\nimpl SelectionHandler for State {\n    type SelectionUserData = Arc<[u8]>;\n\n    fn send_selection(\n        &mut self,\n        _ty: SelectionTarget,\n        _mime_type: String,\n        fd: OwnedFd,\n        _seat: Seat<Self>,\n        user_data: &Self::SelectionUserData,\n    ) {\n        let _span = tracy_client::span!(\"send_selection\");\n\n        let buf = user_data.clone();\n        thread::spawn(move || {\n            // Clear O_NONBLOCK, otherwise File::write_all() will stop halfway.\n            if let Err(err) = fcntl_setfl(&fd, OFlags::empty()) {\n                warn!(\"error clearing flags on selection target fd: {err:?}\");\n            }\n            if let Err(err) = File::from(fd).write_all(&buf) {\n                warn!(\"error writing selection: {err:?}\");\n            }\n        });\n    }\n}\n\nimpl DataDeviceHandler for State {\n    fn data_device_state(&mut self) -> &mut DataDeviceState {\n        &mut self.niri.data_device_state\n    }\n}\n\nimpl WaylandDndGrabHandler for State {\n    fn dnd_requested<S: dnd::Source>(\n        &mut self,\n        source: S,\n        icon: Option<WlSurface>,\n        seat: Seat<Self>,\n        serial: Serial,\n        type_: dnd::GrabType,\n    ) {\n        self.niri.dnd_icon = icon.map(|surface| DndIcon {\n            surface,\n            offset: Point::new(0, 0),\n        });\n\n        match type_ {\n            dnd::GrabType::Pointer => {\n                let pointer = seat.get_pointer().unwrap();\n                let start_data = pointer.grab_start_data().unwrap();\n                let grab =\n                    DnDGrab::new_pointer(&self.niri.display_handle, start_data, source, seat);\n                pointer.set_grab(self, grab, serial, Focus::Keep);\n            }\n            dnd::GrabType::Touch => {\n                let touch = seat.get_touch().unwrap();\n                let start_data = touch.grab_start_data().unwrap();\n                let grab = DnDGrab::new_touch(&self.niri.display_handle, start_data, source, seat);\n                touch.set_grab(self, grab, serial);\n            }\n        }\n\n        // FIXME: more granular\n        self.niri.queue_redraw_all();\n    }\n}\n\nimpl DndGrabHandler for State {\n    fn dropped(\n        &mut self,\n        target: Option<DndTarget<'_, Self>>,\n        validated: bool,\n        _seat: Seat<Self>,\n        location: Point<f64, Logical>,\n    ) {\n        let target: Option<&WlSurface> = target.map(DndTarget::into_inner);\n        trace!(\"dnd dropped, target: {target:?}, validated: {validated}\");\n\n        // End DnD before activating a specific window below so that it takes precedence.\n        self.niri.layout.dnd_end();\n\n        // Activate the target output, since that's how Firefox drag-tab-into-new-window works for\n        // example. On successful drop, additionally activate the target window.\n        let mut activate_output = true;\n        if let Some(target) = validated.then_some(target).flatten() {\n            let root = self.niri.find_root_shell_surface(target);\n            if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&root) {\n                let window = mapped.window.clone();\n                self.niri.layout.activate_window(&window);\n                self.niri.layer_shell_on_demand_focus = None;\n                activate_output = false;\n            }\n        }\n\n        if activate_output {\n            // Find the output from drop coordinates.\n            if let Some((output, _)) = self.niri.output_under(location) {\n                let output = output.clone();\n                self.niri.layout.focus_output(&output);\n            }\n        }\n\n        self.niri.dnd_icon = None;\n        // FIXME: more granular\n        self.niri.queue_redraw_all();\n    }\n}\n\ndelegate_data_device!(State);\n\nimpl PrimarySelectionHandler for State {\n    fn primary_selection_state(&mut self) -> &mut PrimarySelectionState {\n        &mut self.niri.primary_selection_state\n    }\n}\ndelegate_primary_selection!(State);\n\nimpl WlrDataControlHandler for State {\n    fn data_control_state(&mut self) -> &mut WlrDataControlState {\n        &mut self.niri.wlr_data_control_state\n    }\n}\n\ndelegate_data_control!(State);\n\nimpl ExtDataControlHandler for State {\n    fn data_control_state(&mut self) -> &mut ExtDataControlState {\n        &mut self.niri.ext_data_control_state\n    }\n}\n\ndelegate_ext_data_control!(State);\n\nimpl OutputHandler for State {\n    fn output_bound(&mut self, output: Output, wl_output: WlOutput) {\n        foreign_toplevel::on_output_bound(self, &output, &wl_output);\n        ext_workspace::on_output_bound(self, &output, &wl_output);\n    }\n}\ndelegate_output!(State);\n\ndelegate_presentation!(State);\n\nimpl DmabufHandler for State {\n    fn dmabuf_state(&mut self) -> &mut DmabufState {\n        &mut self.niri.dmabuf_state\n    }\n\n    fn dmabuf_imported(\n        &mut self,\n        _global: &DmabufGlobal,\n        dmabuf: Dmabuf,\n        notifier: ImportNotifier,\n    ) {\n        if self.backend.import_dmabuf(&dmabuf) {\n            let _ = notifier.successful::<State>();\n        } else {\n            notifier.failed();\n        }\n    }\n}\ndelegate_dmabuf!(State);\n\nimpl SessionLockHandler for State {\n    fn lock_state(&mut self) -> &mut SessionLockManagerState {\n        &mut self.niri.session_lock_state\n    }\n\n    fn lock(&mut self, confirmation: SessionLocker) {\n        self.niri.lock(confirmation);\n    }\n\n    fn unlock(&mut self) {\n        self.niri.unlock();\n        self.niri.activate_monitors(&mut self.backend);\n        self.niri.notify_activity();\n    }\n\n    fn new_surface(&mut self, surface: LockSurface, output: WlOutput) {\n        let Some(output) = Output::from_resource(&output) else {\n            warn!(\"no Output matching WlOutput\");\n            return;\n        };\n\n        configure_lock_surface(&surface, &output);\n        self.niri.new_lock_surface(surface, &output);\n    }\n}\ndelegate_session_lock!(State);\n\npub fn configure_lock_surface(surface: &LockSurface, output: &Output) {\n    surface.with_pending_state(|states| {\n        let size = output_size(output);\n        states.size = Some(size.to_i32_round());\n    });\n    let scale = output.current_scale();\n    let transform = output.current_transform();\n    let wl_surface = surface.wl_surface();\n    with_states(wl_surface, |data| {\n        send_scale_transform(wl_surface, data, scale, transform);\n    });\n    surface.send_configure();\n}\n\nimpl SecurityContextHandler for State {\n    fn context_created(&mut self, source: SecurityContextListenerSource, context: SecurityContext) {\n        self.niri\n            .event_loop\n            .insert_source(source, move |client, _, state| {\n                trace!(\"inserting a new restricted client, context={context:?}\");\n                state.niri.insert_client(NewClient {\n                    client,\n                    restricted: true,\n                    credentials_unknown: false,\n                });\n            })\n            .unwrap();\n    }\n}\ndelegate_security_context!(State);\n\nimpl IdleNotifierHandler for State {\n    fn idle_notifier_state(&mut self) -> &mut IdleNotifierState<Self> {\n        &mut self.niri.idle_notifier_state\n    }\n}\ndelegate_idle_notify!(State);\n\nimpl IdleInhibitHandler for State {\n    fn inhibit(&mut self, surface: WlSurface) {\n        self.niri.idle_inhibiting_surfaces.insert(surface);\n    }\n\n    fn uninhibit(&mut self, surface: WlSurface) {\n        self.niri.idle_inhibiting_surfaces.remove(&surface);\n    }\n}\ndelegate_idle_inhibit!(State);\n\nimpl ForeignToplevelHandler for State {\n    fn foreign_toplevel_manager_state(&mut self) -> &mut ForeignToplevelManagerState {\n        &mut self.niri.foreign_toplevel_state\n    }\n\n    fn activate(&mut self, wl_surface: WlSurface) {\n        if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {\n            let window = mapped.window.clone();\n            self.niri.layout.activate_window(&window);\n            self.niri.layer_shell_on_demand_focus = None;\n            self.niri.queue_redraw_all();\n        }\n    }\n\n    fn close(&mut self, wl_surface: WlSurface) {\n        if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {\n            mapped.toplevel().send_close();\n        }\n    }\n\n    fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>) {\n        if let Some((mapped, current_output)) = self.niri.layout.find_window_and_output(&wl_surface)\n        {\n            let window = mapped.window.clone();\n\n            if let Some(requested_output) = wl_output.as_ref().and_then(Output::from_resource) {\n                if Some(&requested_output) != current_output {\n                    self.niri.layout.move_to_output(\n                        Some(&window),\n                        &requested_output,\n                        None,\n                        ActivateWindow::Smart,\n                    );\n                }\n            }\n\n            self.niri.layout.set_fullscreen(&window, true);\n        }\n    }\n\n    fn unset_fullscreen(&mut self, wl_surface: WlSurface) {\n        if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {\n            let window = mapped.window.clone();\n            self.niri.layout.set_fullscreen(&window, false);\n        }\n    }\n\n    fn set_maximized(&mut self, wl_surface: WlSurface) {\n        if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {\n            let window = mapped.window.clone();\n            self.niri.layout.set_maximized(&window, true);\n        }\n    }\n\n    fn unset_maximized(&mut self, wl_surface: WlSurface) {\n        if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {\n            let window = mapped.window.clone();\n            self.niri.layout.set_maximized(&window, false);\n        }\n    }\n}\ndelegate_foreign_toplevel!(State);\n\nimpl ExtWorkspaceHandler for State {\n    fn ext_workspace_manager_state(&mut self) -> &mut ExtWorkspaceManagerState {\n        &mut self.niri.ext_workspace_state\n    }\n\n    fn activate_workspace(&mut self, id: WorkspaceId) {\n        let reference = niri_config::WorkspaceReference::Id(id.get());\n        if let Some((mut output, index)) = self.niri.find_output_and_workspace_index(reference) {\n            if let Some(active) = self.niri.layout.active_output() {\n                if output.as_ref() == Some(active) {\n                    output = None;\n                }\n            }\n\n            if let Some(output) = output {\n                self.niri.layout.focus_output(&output);\n            }\n            self.niri.layout.switch_workspace(index);\n            // No mouse warp: assuming the layer-shell bar workspaces use-case.\n\n            // FIXME: granular\n            self.niri.queue_redraw_all();\n        }\n    }\n\n    fn assign_workspace(&mut self, ws_id: WorkspaceId, output: Output) {\n        let reference = niri_config::WorkspaceReference::Id(ws_id.get());\n        if let Some((old_output, old_idx)) = self.niri.find_output_and_workspace_index(reference) {\n            self.niri\n                .layout\n                .move_workspace_to_output_by_id(old_idx, old_output, &output);\n        }\n    }\n}\ndelegate_ext_workspace!(State);\n\nimpl ScreencopyHandler for State {\n    fn frame(&mut self, manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy) {\n        // If with_damage then push it onto the queue for redraw of the output,\n        // otherwise render it immediately.\n        if screencopy.with_damage() {\n            self.niri.screencopy_state.push(manager, screencopy);\n        } else {\n            self.backend.with_primary_renderer(|renderer| {\n                if let Err(err) = self\n                    .niri\n                    .render_for_screencopy_without_damage(renderer, manager, screencopy)\n                {\n                    warn!(\"error rendering for screencopy: {err:?}\");\n                }\n            });\n        }\n    }\n\n    fn screencopy_state(&mut self) -> &mut ScreencopyManagerState {\n        &mut self.niri.screencopy_state\n    }\n}\ndelegate_screencopy!(State);\n\nimpl VirtualPointerHandler for State {\n    fn virtual_pointer_manager_state(&mut self) -> &mut VirtualPointerManagerState {\n        &mut self.niri.virtual_pointer_state\n    }\n\n    fn on_virtual_pointer_motion(&mut self, event: VirtualPointerMotionEvent) {\n        self.process_input_event(InputEvent::<VirtualPointerInputBackend>::PointerMotion { event });\n    }\n\n    fn on_virtual_pointer_motion_absolute(&mut self, event: VirtualPointerMotionAbsoluteEvent) {\n        self.process_input_event(\n            InputEvent::<VirtualPointerInputBackend>::PointerMotionAbsolute { event },\n        );\n    }\n\n    fn on_virtual_pointer_button(&mut self, event: VirtualPointerButtonEvent) {\n        self.process_input_event(InputEvent::<VirtualPointerInputBackend>::PointerButton { event });\n    }\n\n    fn on_virtual_pointer_axis(&mut self, event: VirtualPointerAxisEvent) {\n        self.process_input_event(InputEvent::<VirtualPointerInputBackend>::PointerAxis { event });\n    }\n}\ndelegate_virtual_pointer!(State);\n\nimpl DrmLeaseHandler for State {\n    fn drm_lease_state(&mut self, node: DrmNode) -> &mut DrmLeaseState {\n        self.backend\n            .tty()\n            .get_device_from_node(node)\n            .unwrap()\n            .drm_lease_state\n            .as_mut()\n            .unwrap()\n    }\n\n    fn lease_request(\n        &mut self,\n        node: DrmNode,\n        request: DrmLeaseRequest,\n    ) -> Result<DrmLeaseBuilder, LeaseRejected> {\n        debug!(\n            \"Received lease request for {} connectors\",\n            request.connectors.len()\n        );\n        self.backend\n            .tty()\n            .get_device_from_node(node)\n            .unwrap()\n            .lease_request(request)\n    }\n\n    fn new_active_lease(&mut self, node: DrmNode, lease: DrmLease) {\n        debug!(\"Lease success\");\n        self.backend\n            .tty()\n            .get_device_from_node(node)\n            .unwrap()\n            .new_lease(lease);\n    }\n\n    fn lease_destroyed(&mut self, node: DrmNode, lease_id: u32) {\n        debug!(\"Destroyed lease\");\n        self.backend\n            .tty()\n            .get_device_from_node(node)\n            .unwrap()\n            .remove_lease(lease_id);\n    }\n}\ndelegate_drm_lease!(State);\n\ndelegate_viewporter!(State);\n\nimpl GammaControlHandler for State {\n    fn gamma_control_manager_state(&mut self) -> &mut GammaControlManagerState {\n        &mut self.niri.gamma_control_manager_state\n    }\n\n    fn get_gamma_size(&mut self, output: &Output) -> Option<u32> {\n        match self.backend.tty().get_gamma_size(output) {\n            Ok(0) => None, // Setting gamma is not supported.\n            Ok(size) => Some(size),\n            Err(err) => {\n                warn!(\n                    \"error getting gamma size for output {}: {err:?}\",\n                    output.name()\n                );\n                None\n            }\n        }\n    }\n\n    fn set_gamma(&mut self, output: &Output, ramp: Option<Vec<u16>>) -> Option<()> {\n        match self.backend.tty().set_gamma(output, ramp) {\n            Ok(()) => Some(()),\n            Err(err) => {\n                warn!(\"error setting gamma for output {}: {err:?}\", output.name());\n                None\n            }\n        }\n    }\n}\ndelegate_gamma_control!(State);\n\nstruct UrgentOnlyMarker;\n\nimpl XdgActivationHandler for State {\n    fn activation_state(&mut self) -> &mut XdgActivationState {\n        &mut self.niri.activation_state\n    }\n\n    fn token_created(&mut self, _token: XdgActivationToken, data: XdgActivationTokenData) -> bool {\n        // Tokens without a serial are urgency-only. This is not specified, but it seems to be the\n        // common client behavior.\n        //\n        // See also: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/150\n        let Some((serial, seat)) = data.serial else {\n            data.user_data.insert_if_missing(|| UrgentOnlyMarker);\n            return true;\n        };\n        let Some(seat) = Seat::<State>::from_resource(&seat) else {\n            return false;\n        };\n\n        // Widely-used clients such as Discord and Telegram make new tokens (with invalid serials)\n        // upon clicking on their tray icon or on their notification. This debug flag makes that\n        // work.\n        //\n        // Clicking on a notification sends clients a perfectly valid activation token from the\n        // notification daemon, but alas they ignore it. Maybe in the future the clients are fixed,\n        // and we can remove this debug flag.\n        let config = self.niri.config.borrow();\n        if config.debug.honor_xdg_activation_with_invalid_serial {\n            return true;\n        }\n\n        // Check the serial against both a keyboard and a pointer, since layer-shell surfaces\n        // with no keyboard interactivity won't have any keyboard focus.\n        let kb_last_enter = seat.get_keyboard().unwrap().last_enter();\n        if kb_last_enter.is_some_and(|last_enter| serial.is_no_older_than(&last_enter)) {\n            return true;\n        }\n\n        let pointer_last_enter = seat.get_pointer().unwrap().last_enter();\n        if pointer_last_enter.is_some_and(|last_enter| serial.is_no_older_than(&last_enter)) {\n            return true;\n        }\n\n        false\n    }\n\n    fn request_activation(\n        &mut self,\n        token: XdgActivationToken,\n        token_data: XdgActivationTokenData,\n        surface: WlSurface,\n    ) {\n        if token_data.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT {\n            if let Some((mapped, _)) = self.niri.layout.find_window_and_output_mut(&surface) {\n                let window = mapped.window.clone();\n                if token_data.user_data.get::<UrgentOnlyMarker>().is_some() {\n                    mapped.set_urgent(true);\n                    self.niri.queue_redraw_all();\n                } else {\n                    self.niri.layout.activate_window(&window);\n                    self.niri.layer_shell_on_demand_focus = None;\n                    self.niri.queue_redraw_all();\n                }\n            } else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(&surface) {\n                unmapped.activation_token_data = Some(token_data);\n            }\n        }\n\n        self.niri.activation_state.remove_token(&token);\n    }\n}\ndelegate_xdg_activation!(State);\n\nimpl FractionalScaleHandler for State {}\ndelegate_fractional_scale!(State);\n\nimpl OutputManagementHandler for State {\n    fn output_management_state(&mut self) -> &mut OutputManagementManagerState {\n        &mut self.niri.output_management_state\n    }\n\n    fn apply_output_config(&mut self, config: niri_config::Outputs) {\n        self.niri.config.borrow_mut().outputs = config;\n        self.reload_output_config();\n    }\n}\ndelegate_output_management!(State);\n\nimpl MutterX11InteropHandler for State {}\ndelegate_mutter_x11_interop!(State);\n\ndelegate_single_pixel_buffer!(State);\n"
  },
  {
    "path": "src/handlers/xdg_shell.rs",
    "content": "use std::cell::Cell;\n\nuse calloop::Interest;\nuse niri_config::PresetSize;\nuse smithay::desktop::{\n    find_popup_root_surface, get_popup_toplevel_coords, layer_map_for_output, utils, LayerSurface,\n    PopupKeyboardGrab, PopupKind, PopupManager, PopupPointerGrab, PopupUngrabStrategy, Window,\n    WindowSurfaceType,\n};\nuse smithay::input::pointer::Focus;\nuse smithay::output::Output;\nuse smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_positioner::ConstraintAdjustment;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::{self};\nuse smithay::reexports::wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration;\nuse smithay::reexports::wayland_server::protocol::wl_output;\nuse smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::reexports::wayland_server::{self, Resource, WEnum};\nuse smithay::utils::{Logical, Rectangle, Serial};\nuse smithay::wayland::compositor::{\n    add_blocker, add_pre_commit_hook, with_states, BufferAssignment, CompositorHandler as _,\n    HookId, SurfaceAttributes,\n};\nuse smithay::wayland::dmabuf::get_dmabuf;\nuse smithay::wayland::input_method::InputMethodSeat;\nuse smithay::wayland::shell::kde::decoration::{KdeDecorationHandler, KdeDecorationState};\nuse smithay::wayland::shell::wlr_layer::{self, Layer};\nuse smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;\nuse smithay::wayland::shell::xdg::{\n    PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,\n    XdgToplevelSurfaceData,\n};\nuse smithay::wayland::xdg_foreign::{XdgForeignHandler, XdgForeignState};\nuse smithay::{\n    delegate_kde_decoration, delegate_xdg_decoration, delegate_xdg_foreign, delegate_xdg_shell,\n};\nuse tracing::field::Empty;\n\nuse crate::input::move_grab::MoveGrab;\nuse crate::input::resize_grab::ResizeGrab;\nuse crate::input::touch_resize_grab::TouchResizeGrab;\nuse crate::input::{PointerOrTouchStartData, DOUBLE_CLICK_TIME};\nuse crate::layout::ActivateWindow;\nuse crate::niri::{CastTarget, PopupGrabState, State};\nuse crate::utils::transaction::Transaction;\nuse crate::utils::{\n    get_monotonic_time, output_matches_name, send_scale_transform, update_tiled_state, ResizeEdge,\n};\nuse crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped, WindowRef};\n\nimpl XdgShellHandler for State {\n    fn xdg_shell_state(&mut self) -> &mut XdgShellState {\n        &mut self.niri.xdg_shell_state\n    }\n\n    fn new_toplevel(&mut self, surface: ToplevelSurface) {\n        let wl_surface = surface.wl_surface().clone();\n        let unmapped = Unmapped::new(Window::new_wayland_window(surface));\n        let existing = self.niri.unmapped_windows.insert(wl_surface, unmapped);\n        assert!(existing.is_none());\n    }\n\n    fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {\n        let popup = PopupKind::Xdg(surface);\n        self.unconstrain_popup(&popup);\n\n        if let Err(err) = self.niri.popups.track_popup(popup) {\n            warn!(\"error tracking popup: {err:?}\");\n        }\n    }\n\n    fn move_request(&mut self, surface: ToplevelSurface, _seat: WlSeat, serial: Serial) {\n        let wl_surface = surface.wl_surface();\n\n        let mut grab_start_data = None;\n\n        // See if this comes from a pointer grab.\n        let pointer = self.niri.seat.get_pointer().unwrap();\n        pointer.with_grab(|grab_serial, grab| {\n            if grab_serial == serial {\n                let start_data = grab.start_data();\n                if let Some((focus, _)) = &start_data.focus {\n                    if focus.id().same_client_as(&wl_surface.id()) {\n                        // Deny move requests from DnD grabs to work around\n                        // https://gitlab.gnome.org/GNOME/gtk/-/issues/7113\n                        let is_dnd_grab = Self::is_dnd_grab(grab.as_any());\n\n                        if !is_dnd_grab {\n                            grab_start_data =\n                                Some(PointerOrTouchStartData::Pointer(start_data.clone()));\n                        }\n                    }\n                }\n            }\n        });\n\n        // See if this comes from a touch grab.\n        if let Some(touch) = self.niri.seat.get_touch() {\n            touch.with_grab(|grab_serial, grab| {\n                if grab_serial == serial {\n                    let start_data = grab.start_data();\n                    if let Some((focus, _)) = &start_data.focus {\n                        if focus.id().same_client_as(&wl_surface.id()) {\n                            // Deny move requests from DnD grabs to work around\n                            // https://gitlab.gnome.org/GNOME/gtk/-/issues/7113\n                            let is_dnd_grab = Self::is_dnd_grab(grab.as_any());\n\n                            if !is_dnd_grab {\n                                grab_start_data =\n                                    Some(PointerOrTouchStartData::Touch(start_data.clone()));\n                            }\n                        }\n                    }\n                }\n            });\n        }\n\n        let Some(start_data) = grab_start_data else {\n            return;\n        };\n\n        let Some((mapped, output)) = self.niri.layout.find_window_and_output(wl_surface) else {\n            return;\n        };\n\n        let Some(output) = output else {\n            return;\n        };\n\n        let window = mapped.window.clone();\n        let output = output.clone();\n\n        match &start_data {\n            PointerOrTouchStartData::Pointer(_) => {\n                if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true, None) {\n                    pointer.set_grab(self, grab, serial, Focus::Clear);\n                }\n            }\n            PointerOrTouchStartData::Touch(_) => {\n                let touch = self.niri.seat.get_touch().unwrap();\n                if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true, None) {\n                    touch.set_grab(self, grab, serial);\n                }\n            }\n        }\n\n        self.niri.queue_redraw(&output);\n    }\n\n    fn resize_request(\n        &mut self,\n        surface: ToplevelSurface,\n        _seat: WlSeat,\n        serial: Serial,\n        edges: xdg_toplevel::ResizeEdge,\n    ) {\n        let wl_surface = surface.wl_surface();\n\n        let mut grab_start_data = None;\n\n        // See if this comes from a pointer grab.\n        let pointer = self.niri.seat.get_pointer().unwrap();\n        if pointer.has_grab(serial) {\n            if let Some(start_data) = pointer.grab_start_data() {\n                if let Some((focus, _)) = &start_data.focus {\n                    if focus.id().same_client_as(&wl_surface.id()) {\n                        grab_start_data = Some(PointerOrTouchStartData::Pointer(start_data));\n                    }\n                }\n            }\n        }\n\n        // See if this comes from a touch grab.\n        if let Some(touch) = self.niri.seat.get_touch() {\n            if touch.has_grab(serial) {\n                if let Some(start_data) = touch.grab_start_data() {\n                    if let Some((focus, _)) = &start_data.focus {\n                        if focus.id().same_client_as(&wl_surface.id()) {\n                            grab_start_data = Some(PointerOrTouchStartData::Touch(start_data));\n                        }\n                    }\n                }\n            }\n        }\n\n        let Some(start_data) = grab_start_data else {\n            return;\n        };\n\n        let Some((mapped, _)) = self.niri.layout.find_window_and_output(wl_surface) else {\n            return;\n        };\n\n        let edges = ResizeEdge::from(edges);\n        let window = mapped.window.clone();\n\n        // See if we got a double resize-click gesture.\n        let time = get_monotonic_time();\n        let last_cell = mapped.last_interactive_resize_start();\n        let mut last = last_cell.get();\n        last_cell.set(Some((time, edges)));\n\n        // Floating windows don't have either of the double-resize-click gestures, so just allow it\n        // to resize.\n        if mapped.is_floating() {\n            last = None;\n            last_cell.set(None);\n        }\n\n        if let Some((last_time, last_edges)) = last {\n            if time.saturating_sub(last_time) <= DOUBLE_CLICK_TIME {\n                // Allow quick resize after a triple click.\n                last_cell.set(None);\n\n                let intersection = edges.intersection(last_edges);\n                if intersection.intersects(ResizeEdge::LEFT_RIGHT) {\n                    // FIXME: don't activate once we can pass specific windows to actions.\n                    self.niri.layout.activate_window(&window);\n                    self.niri.layer_shell_on_demand_focus = None;\n                    self.niri.layout.toggle_full_width();\n                }\n                if intersection.intersects(ResizeEdge::TOP_BOTTOM) {\n                    self.niri.layer_shell_on_demand_focus = None;\n                    self.niri.layout.reset_window_height(Some(&window));\n                }\n                // FIXME: granular.\n                self.niri.queue_redraw_all();\n                return;\n            }\n        }\n\n        if !self\n            .niri\n            .layout\n            .interactive_resize_begin(window.clone(), edges)\n        {\n            return;\n        }\n\n        match start_data {\n            PointerOrTouchStartData::Pointer(start_data) => {\n                let grab = ResizeGrab::new(start_data, window);\n                pointer.set_grab(self, grab, serial, Focus::Clear);\n            }\n            PointerOrTouchStartData::Touch(start_data) => {\n                let touch = self.niri.seat.get_touch().unwrap();\n                let grab = TouchResizeGrab::new(start_data, window);\n                touch.set_grab(self, grab, serial);\n            }\n        }\n    }\n\n    fn reposition_request(\n        &mut self,\n        surface: PopupSurface,\n        positioner: PositionerState,\n        token: u32,\n    ) {\n        surface.with_pending_state(|state| {\n            let geometry = positioner.get_geometry();\n            state.geometry = geometry;\n            state.positioner = positioner;\n        });\n        self.unconstrain_popup(&PopupKind::Xdg(surface.clone()));\n        surface.send_repositioned(token);\n    }\n\n    fn grab(&mut self, surface: PopupSurface, _seat: WlSeat, serial: Serial) {\n        let popup = PopupKind::Xdg(surface);\n        let Ok(root) = find_popup_root_surface(&popup) else {\n            trace!(\"ignoring popup grab because no root surface\");\n            return;\n        };\n\n        // We need to hand out the grab in a way consistent with what update_keyboard_focus()\n        // thinks the current focus is, otherwise it will desync and cause weird issues with\n        // keyboard focus being at the wrong place.\n        if self.niri.exit_confirm_dialog.is_open() {\n            trace!(\"ignoring popup grab because the exit confirm dialog is open\");\n            let _ = PopupManager::dismiss_popup(&root, &popup);\n            return;\n        } else if self.niri.is_locked() {\n            if Some(&root) != self.niri.lock_surface_focus().as_ref() {\n                trace!(\"ignoring popup grab because the session is locked\");\n                let _ = PopupManager::dismiss_popup(&root, &popup);\n                return;\n            }\n        } else if self.niri.screenshot_ui.is_open() {\n            trace!(\"ignoring popup grab because the screenshot UI is open\");\n            let _ = PopupManager::dismiss_popup(&root, &popup);\n            return;\n        } else if let Some(output) = self.niri.layout.active_output() {\n            let layers = layer_map_for_output(output);\n\n            // FIXME: somewhere here we probably need to check is_overview_open to match the logic\n            // in update_keyboard_focus().\n\n            if let Some(layer) = layers.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL) {\n                // This is a grab for a layer surface.\n\n                if let Some(mapped) = self.niri.mapped_layer_surfaces.get(layer) {\n                    if mapped.place_within_backdrop() {\n                        trace!(\"ignoring popup grab for a layer surface within overview backdrop\");\n                        let _ = PopupManager::dismiss_popup(&root, &popup);\n                        return;\n                    }\n                }\n            } else {\n                // This is a grab for a regular window; check that there's no layer surface with a\n                // higher input priority.\n\n                if layers.layers_on(Layer::Overlay).any(|l| {\n                    (l.cached_state().keyboard_interactivity\n                        == wlr_layer::KeyboardInteractivity::Exclusive\n                        || Some(l) == self.niri.layer_shell_on_demand_focus.as_ref())\n                        && self.niri.mapped_layer_surfaces.contains_key(l)\n                }) {\n                    trace!(\"ignoring toplevel popup grab because the overlay layer has focus\");\n                    let _ = PopupManager::dismiss_popup(&root, &popup);\n                    return;\n                }\n\n                let mon = self.niri.layout.monitor_for_output(output).unwrap();\n                if !mon.render_above_top_layer()\n                    && layers.layers_on(Layer::Top).any(|l| {\n                        (l.cached_state().keyboard_interactivity\n                            == wlr_layer::KeyboardInteractivity::Exclusive\n                            || Some(l) == self.niri.layer_shell_on_demand_focus.as_ref())\n                            && self.niri.mapped_layer_surfaces.contains_key(l)\n                    })\n                {\n                    trace!(\"ignoring toplevel popup grab because the top layer has focus\");\n                    let _ = PopupManager::dismiss_popup(&root, &popup);\n                    return;\n                }\n\n                let layout_focus = self.niri.layout.focus();\n                if Some(&root) != layout_focus.map(|win| win.toplevel().wl_surface()) {\n                    trace!(\"ignoring toplevel popup grab because another window has focus\");\n                    let _ = PopupManager::dismiss_popup(&root, &popup);\n                    return;\n                }\n            }\n        } else {\n            trace!(\"ignoring popup grab because no output is active\");\n            let _ = PopupManager::dismiss_popup(&root, &popup);\n            return;\n        }\n\n        let seat = &self.niri.seat;\n        let mut grab = match self\n            .niri\n            .popups\n            .grab_popup(root.clone(), popup, seat, serial)\n        {\n            Ok(grab) => grab,\n            Err(err) => {\n                trace!(\"ignoring popup grab: {err:?}\");\n                return;\n            }\n        };\n\n        let keyboard = seat.get_keyboard().unwrap();\n        let pointer = seat.get_pointer().unwrap();\n\n        // Smithay cannot do overlapping grabs, so if we have an IME keyboard grab, don't overwrite\n        // it with a popup keyboard grab. This makes the popup menu work in Telegram while an IME\n        // is active (otherwise it hits the grab mismatch check below).\n        //\n        // The second check is for layer surfaces that can't receive keyboard focus, without it\n        // popups don't work properly in Waybar (GTK 3).\n        let can_receive_keyboard_focus = !self.niri.seat.input_method().keyboard_grabbed()\n            && self\n                .niri\n                .layout\n                .active_output()\n                .and_then(|output| {\n                    layer_map_for_output(output)\n                        .layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)\n                        .map(|layer_surface| layer_surface.can_receive_keyboard_focus())\n                })\n                .unwrap_or(true);\n\n        let keyboard_grab_mismatches = keyboard.is_grabbed()\n            && !(keyboard.has_grab(serial)\n                || grab.previous_serial().is_none_or(|s| keyboard.has_grab(s)));\n        let pointer_grab_mismatches = pointer.is_grabbed()\n            && !(pointer.has_grab(serial)\n                || grab.previous_serial().is_none_or(|s| pointer.has_grab(s)));\n        if (can_receive_keyboard_focus && keyboard_grab_mismatches) || pointer_grab_mismatches {\n            trace!(\"ignoring popup grab because of current grab mismatch\");\n            grab.ungrab(PopupUngrabStrategy::All);\n            return;\n        }\n\n        trace!(\"new grab for root {:?}\", root);\n        if can_receive_keyboard_focus {\n            keyboard.set_grab(self, PopupKeyboardGrab::new(&grab), serial);\n        }\n        pointer.set_grab(self, PopupPointerGrab::new(&grab), serial, Focus::Keep);\n        self.niri.popup_grab = Some(PopupGrabState {\n            root,\n            grab,\n            has_keyboard_grab: can_receive_keyboard_focus,\n        });\n    }\n\n    fn maximize_request(&mut self, toplevel: ToplevelSurface) {\n        if let Some((mapped, _)) = self\n            .niri\n            .layout\n            .find_window_and_output_mut(toplevel.wl_surface())\n        {\n            // A configure is required in response to this event regardless if there are pending\n            // changes.\n            mapped.set_needs_configure();\n\n            let window = mapped.window.clone();\n            self.niri.layout.set_maximized(&window, true);\n        } else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {\n            match &mut unmapped.state {\n                InitialConfigureState::NotConfigured {\n                    wants_maximized, ..\n                } => {\n                    *wants_maximized = true;\n\n                    // The required configure will be the initial configure.\n                }\n                InitialConfigureState::Configured {\n                    rules,\n                    output,\n                    is_pending_maximized,\n                    ..\n                } => {\n                    // Figure out the monitor following a similar logic to initial configure.\n                    // FIXME: deduplicate.\n                    let mon = output\n                        .as_ref()\n                        .and_then(|o| self.niri.layout.monitor_for_output(o))\n                        .map(|mon| (mon, false))\n                        // If not, check if we have a parent with a monitor.\n                        .or_else(|| {\n                            toplevel\n                                .parent()\n                                .and_then(|parent| self.niri.layout.find_window_and_output(&parent))\n                                .and_then(|(_win, output)| output)\n                                .and_then(|o| self.niri.layout.monitor_for_output(o))\n                                .map(|mon| (mon, true))\n                        })\n                        // If not, fall back to the active monitor.\n                        .or_else(|| {\n                            self.niri\n                                .layout\n                                .active_monitor_ref()\n                                .map(|mon| (mon, false))\n                        });\n\n                    *output = mon\n                        .filter(|(_, parent)| !parent)\n                        .map(|(mon, _)| mon.output().clone());\n                    let mon = mon.map(|(mon, _)| mon);\n\n                    let ws = mon\n                        .map(|mon| mon.active_workspace_ref())\n                        .or_else(|| self.niri.layout.active_workspace());\n\n                    if let Some(ws) = ws {\n                        // If the window is pending fullscreen, then this will do nothing. But\n                        // that's expected: the window remains fullscreen, and we simply remember\n                        // that it is now pending maximized.\n                        *is_pending_maximized = true;\n                        toplevel.with_pending_state(|state| {\n                            if !state.states.contains(xdg_toplevel::State::Fullscreen) {\n                                state.states.set(xdg_toplevel::State::Maximized);\n                            }\n                        });\n                        ws.configure_new_window(&unmapped.window, None, None, false, rules);\n                    }\n\n                    // We already sent the initial configure, so we need to reconfigure.\n                    toplevel.send_configure();\n                }\n            }\n        } else {\n            error!(\"couldn't find the toplevel in maximize_request()\");\n            toplevel.send_configure();\n        }\n    }\n\n    fn unmaximize_request(&mut self, toplevel: ToplevelSurface) {\n        if let Some((mapped, _)) = self\n            .niri\n            .layout\n            .find_window_and_output_mut(toplevel.wl_surface())\n        {\n            // A configure is required in response to this event regardless if there are pending\n            // changes.\n            mapped.set_needs_configure();\n\n            let window = mapped.window.clone();\n            self.niri.layout.set_maximized(&window, false);\n        } else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {\n            match &mut unmapped.state {\n                InitialConfigureState::NotConfigured {\n                    wants_maximized, ..\n                } => {\n                    *wants_maximized = false;\n\n                    // The required configure will be the initial configure.\n                }\n                InitialConfigureState::Configured {\n                    rules,\n                    width,\n                    height,\n                    floating_width,\n                    floating_height,\n                    is_full_width,\n                    output,\n                    workspace_name,\n                    is_pending_maximized,\n                } => {\n                    // Figure out the monitor following a similar logic to initial configure.\n                    // FIXME: deduplicate.\n                    let mon = workspace_name\n                        .as_deref()\n                        .and_then(|name| self.niri.layout.monitor_for_workspace(name))\n                        .map(|mon| (mon, false));\n\n                    let mon = mon.or_else(|| {\n                        output\n                            .as_ref()\n                            .and_then(|o| self.niri.layout.monitor_for_output(o))\n                            .map(|mon| (mon, false))\n                            // If not, check if we have a parent with a monitor.\n                            .or_else(|| {\n                                toplevel\n                                    .parent()\n                                    .and_then(|parent| {\n                                        self.niri.layout.find_window_and_output(&parent)\n                                    })\n                                    .and_then(|(_win, output)| output)\n                                    .and_then(|o| self.niri.layout.monitor_for_output(o))\n                                    .map(|mon| (mon, true))\n                            })\n                            // If not, fall back to the active monitor.\n                            .or_else(|| {\n                                self.niri\n                                    .layout\n                                    .active_monitor_ref()\n                                    .map(|mon| (mon, false))\n                            })\n                    });\n\n                    *output = mon\n                        .filter(|(_, parent)| !parent)\n                        .map(|(mon, _)| mon.output().clone());\n                    let mon = mon.map(|(mon, _)| mon);\n\n                    let ws = workspace_name\n                        .as_deref()\n                        .and_then(|name| mon.map(|mon| mon.find_named_workspace(name)))\n                        .unwrap_or_else(|| {\n                            mon.map(|mon| mon.active_workspace_ref())\n                                .or_else(|| self.niri.layout.active_workspace())\n                        });\n\n                    if let Some(ws) = ws {\n                        // If the window is pending fullscreen, then this will do nothing since\n                        // then the Maximized state is already unset. But that's expected: the\n                        // window remains fullscreen, and we simply remember that it is no\n                        // longer pending maximized.\n                        *is_pending_maximized = false;\n                        toplevel.with_pending_state(|state| {\n                            state.states.unset(xdg_toplevel::State::Maximized);\n                        });\n\n                        let is_floating = rules.compute_open_floating(&toplevel);\n                        let configure_width = if is_floating {\n                            *floating_width\n                        } else if *is_full_width {\n                            Some(PresetSize::Proportion(1.))\n                        } else {\n                            *width\n                        };\n                        let configure_height = if is_floating {\n                            *floating_height\n                        } else {\n                            *height\n                        };\n                        ws.configure_new_window(\n                            &unmapped.window,\n                            configure_width,\n                            configure_height,\n                            is_floating,\n                            rules,\n                        );\n                    }\n\n                    // We already sent the initial configure, so we need to reconfigure.\n                    toplevel.send_configure();\n                }\n            }\n        } else {\n            error!(\"couldn't find the toplevel in unmaximize_request()\");\n            toplevel.send_configure();\n        }\n    }\n\n    fn fullscreen_request(\n        &mut self,\n        toplevel: ToplevelSurface,\n        wl_output: Option<wl_output::WlOutput>,\n    ) {\n        let requested_output = wl_output.as_ref().and_then(Output::from_resource);\n\n        if let Some((mapped, current_output)) = self\n            .niri\n            .layout\n            .find_window_and_output_mut(toplevel.wl_surface())\n        {\n            // A configure is required in response to this event regardless if there are pending\n            // changes.\n            mapped.set_needs_configure();\n\n            let window = mapped.window.clone();\n\n            if let Some(requested_output) = requested_output {\n                if Some(&requested_output) != current_output {\n                    self.niri.layout.move_to_output(\n                        Some(&window),\n                        &requested_output,\n                        None,\n                        ActivateWindow::Smart,\n                    );\n                }\n            }\n\n            self.niri.layout.set_fullscreen(&window, true);\n        } else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {\n            match &mut unmapped.state {\n                InitialConfigureState::NotConfigured {\n                    wants_fullscreen, ..\n                } => {\n                    *wants_fullscreen = Some(requested_output);\n\n                    // The required configure will be the initial configure.\n                }\n                InitialConfigureState::Configured { rules, output, .. } => {\n                    // Figure out the monitor following a similar logic to initial configure.\n                    // FIXME: deduplicate.\n                    let mon = requested_output\n                        .as_ref()\n                        // If none requested, try currently configured output.\n                        .or(output.as_ref())\n                        .and_then(|o| self.niri.layout.monitor_for_output(o))\n                        .map(|mon| (mon, false))\n                        // If not, check if we have a parent with a monitor.\n                        .or_else(|| {\n                            toplevel\n                                .parent()\n                                .and_then(|parent| self.niri.layout.find_window_and_output(&parent))\n                                .and_then(|(_win, output)| output)\n                                .and_then(|o| self.niri.layout.monitor_for_output(o))\n                                .map(|mon| (mon, true))\n                        })\n                        // If not, fall back to the active monitor.\n                        .or_else(|| {\n                            self.niri\n                                .layout\n                                .active_monitor_ref()\n                                .map(|mon| (mon, false))\n                        });\n\n                    *output = mon\n                        .filter(|(_, parent)| !parent)\n                        .map(|(mon, _)| mon.output().clone());\n                    let mon = mon.map(|(mon, _)| mon);\n\n                    let ws = mon\n                        .map(|mon| mon.active_workspace_ref())\n                        .or_else(|| self.niri.layout.active_workspace());\n\n                    if let Some(ws) = ws {\n                        toplevel.with_pending_state(|state| {\n                            state.states.set(xdg_toplevel::State::Fullscreen);\n                            state.states.unset(xdg_toplevel::State::Maximized);\n                        });\n                        ws.configure_new_window(&unmapped.window, None, None, false, rules);\n                    }\n\n                    // We already sent the initial configure, so we need to reconfigure.\n                    toplevel.send_configure();\n                }\n            }\n        } else {\n            error!(\"couldn't find the toplevel in fullscreen_request()\");\n            toplevel.send_configure();\n        }\n    }\n\n    fn unfullscreen_request(&mut self, toplevel: ToplevelSurface) {\n        if let Some((mapped, _)) = self\n            .niri\n            .layout\n            .find_window_and_output_mut(toplevel.wl_surface())\n        {\n            // A configure is required in response to this event regardless if there are pending\n            // changes.\n            mapped.set_needs_configure();\n\n            let window = mapped.window.clone();\n            self.niri.layout.set_fullscreen(&window, false);\n        } else if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {\n            match &mut unmapped.state {\n                InitialConfigureState::NotConfigured {\n                    wants_fullscreen, ..\n                } => {\n                    *wants_fullscreen = None;\n\n                    // The required configure will be the initial configure.\n                }\n                InitialConfigureState::Configured {\n                    rules,\n                    width,\n                    height,\n                    floating_width,\n                    floating_height,\n                    is_full_width,\n                    output,\n                    workspace_name,\n                    is_pending_maximized,\n                } => {\n                    // Figure out the monitor following a similar logic to initial configure.\n                    // FIXME: deduplicate.\n                    let mon = workspace_name\n                        .as_deref()\n                        .and_then(|name| self.niri.layout.monitor_for_workspace(name))\n                        .map(|mon| (mon, false));\n\n                    let mon = mon.or_else(|| {\n                        output\n                            .as_ref()\n                            .and_then(|o| self.niri.layout.monitor_for_output(o))\n                            .map(|mon| (mon, false))\n                            // If not, check if we have a parent with a monitor.\n                            .or_else(|| {\n                                toplevel\n                                    .parent()\n                                    .and_then(|parent| {\n                                        self.niri.layout.find_window_and_output(&parent)\n                                    })\n                                    .and_then(|(_win, output)| output)\n                                    .and_then(|o| self.niri.layout.monitor_for_output(o))\n                                    .map(|mon| (mon, true))\n                            })\n                            // If not, fall back to the active monitor.\n                            .or_else(|| {\n                                self.niri\n                                    .layout\n                                    .active_monitor_ref()\n                                    .map(|mon| (mon, false))\n                            })\n                    });\n\n                    *output = mon\n                        .filter(|(_, parent)| !parent)\n                        .map(|(mon, _)| mon.output().clone());\n                    let mon = mon.map(|(mon, _)| mon);\n\n                    let ws = workspace_name\n                        .as_deref()\n                        .and_then(|name| mon.map(|mon| mon.find_named_workspace(name)))\n                        .unwrap_or_else(|| {\n                            mon.map(|mon| mon.active_workspace_ref())\n                                .or_else(|| self.niri.layout.active_workspace())\n                        });\n\n                    if let Some(ws) = ws {\n                        toplevel.with_pending_state(|state| {\n                            state.states.unset(xdg_toplevel::State::Fullscreen);\n\n                            if *is_pending_maximized {\n                                state.states.set(xdg_toplevel::State::Maximized);\n                            }\n                        });\n\n                        let is_floating = rules.compute_open_floating(&toplevel);\n                        let configure_width = if is_floating {\n                            *floating_width\n                        } else if *is_full_width {\n                            Some(PresetSize::Proportion(1.))\n                        } else {\n                            *width\n                        };\n                        let configure_height = if is_floating {\n                            *floating_height\n                        } else {\n                            *height\n                        };\n                        ws.configure_new_window(\n                            &unmapped.window,\n                            configure_width,\n                            configure_height,\n                            is_floating,\n                            rules,\n                        );\n                    }\n\n                    // We already sent the initial configure, so we need to reconfigure.\n                    toplevel.send_configure();\n                }\n            }\n        } else {\n            error!(\"couldn't find the toplevel in unfullscreen_request()\");\n            toplevel.send_configure();\n        }\n    }\n\n    fn toplevel_destroyed(&mut self, surface: ToplevelSurface) {\n        if self\n            .niri\n            .unmapped_windows\n            .remove(surface.wl_surface())\n            .is_some()\n        {\n            // An unmapped toplevel got destroyed.\n            return;\n        }\n\n        let win_out = self\n            .niri\n            .layout\n            .find_window_and_output(surface.wl_surface());\n\n        let Some((mapped, output)) = win_out else {\n            // I have no idea how this can happen, but I saw it happen once, in a weird interaction\n            // involving laptop going to sleep and resuming.\n            error!(\"toplevel missing from both unmapped_windows and layout\");\n            return;\n        };\n        let window = mapped.window.clone();\n        let output = output.cloned();\n\n        let id = mapped.id();\n        self.niri\n            .stop_casts_for_target(CastTarget::Window { id: id.get() });\n\n        self.backend.with_primary_renderer(|renderer| {\n            self.niri.layout.store_unmap_snapshot(renderer, &window);\n        });\n\n        let transaction = Transaction::new();\n        let blocker = transaction.blocker();\n        self.backend.with_primary_renderer(|renderer| {\n            self.niri\n                .layout\n                .start_close_animation_for_window(renderer, &window, blocker);\n        });\n\n        let active_window = self.niri.layout.focus().map(|m| &m.window);\n        let was_active = active_window == Some(&window);\n\n        self.niri.window_mru_ui.remove_window(id);\n        self.niri.layout.remove_window(&window, transaction.clone());\n\n        let surface = surface.wl_surface();\n        // This check is necessary because implicit resource destruction is done with\n        // undefined order, so the surface might get destroyed before toplevel_destroyed() is\n        // called. In this case, adding the default pre-commit hook here would leak it, since the\n        // place that removes it is WlSurface::destroyed(), which had already been called by now.\n        if surface.is_alive() {\n            self.add_default_dmabuf_pre_commit_hook(surface);\n        }\n\n        // If this is the only instance, then this transaction will complete immediately, so no\n        // need to set the timer.\n        if !transaction.is_last() {\n            transaction.register_deadline_timer(&self.niri.event_loop);\n        }\n\n        if was_active {\n            self.maybe_warp_cursor_to_focus();\n        }\n\n        if let Some(output) = output {\n            self.niri.queue_redraw(&output);\n            self.niri.queue_redraw_mru_output();\n        }\n    }\n\n    fn popup_destroyed(&mut self, surface: PopupSurface) {\n        if let Some(output) = self.output_for_popup(&PopupKind::Xdg(surface)) {\n            self.niri.queue_redraw(&output.clone());\n        }\n    }\n\n    fn app_id_changed(&mut self, toplevel: ToplevelSurface) {\n        self.update_window_rules(&toplevel);\n    }\n\n    fn title_changed(&mut self, toplevel: ToplevelSurface) {\n        self.update_window_rules(&toplevel);\n    }\n\n    fn parent_changed(&mut self, toplevel: ToplevelSurface) {\n        let Some(parent) = toplevel.parent() else {\n            return;\n        };\n\n        if let Some((mapped, output)) = self.niri.layout.find_window_and_output_mut(&parent) {\n            let output = output.cloned();\n            let window = mapped.window.clone();\n            if self.niri.layout.descendants_added(&window) {\n                if let Some(output) = output {\n                    self.niri.queue_redraw(&output);\n                }\n            }\n        }\n    }\n}\n\ndelegate_xdg_shell!(State);\n\nimpl XdgDecorationHandler for State {\n    fn new_decoration(&mut self, toplevel: ToplevelSurface) {\n        // If we want CSD, we hide this global altogether.\n        toplevel.with_pending_state(|state| {\n            state.decoration_mode = Some(zxdg_toplevel_decoration_v1::Mode::ServerSide);\n        });\n    }\n\n    fn request_mode(&mut self, toplevel: ToplevelSurface, mode: zxdg_toplevel_decoration_v1::Mode) {\n        // Set whatever the client wants, rather than our preferred mode. This especially matters\n        // for SDL2 which has a bug where forcing a different (client-side) decoration mode during\n        // their window creation sequence would leave the window permanently hidden.\n        //\n        // https://github.com/libsdl-org/SDL/issues/8173\n        //\n        // The bug has been fixed, but there's a ton of apps which will use the buggy version for a\n        // long while...\n        toplevel.with_pending_state(|state| {\n            state.decoration_mode = Some(mode);\n        });\n\n        // A configure is required in response to this event. However, if an initial configure\n        // wasn't sent, then we will send this as part of the initial configure later.\n        if toplevel.is_initial_configure_sent() {\n            // If this is a mapped window, flag it as needs configure to avoid duplicate configures.\n            let surface = toplevel.wl_surface();\n            if let Some((mapped, _)) = self.niri.layout.find_window_and_output_mut(surface) {\n                mapped.set_needs_configure();\n            } else {\n                toplevel.send_configure();\n            }\n        }\n    }\n\n    fn unset_mode(&mut self, toplevel: ToplevelSurface) {\n        // If we want CSD, we hide this global altogether.\n        toplevel.with_pending_state(|state| {\n            state.decoration_mode = Some(zxdg_toplevel_decoration_v1::Mode::ServerSide);\n        });\n\n        // A configure is required in response to this event. However, if an initial configure\n        // wasn't sent, then we will send this as part of the initial configure later.\n        if toplevel.is_initial_configure_sent() {\n            // If this is a mapped window, flag it as needs configure to avoid duplicate configures.\n            let surface = toplevel.wl_surface();\n            if let Some((mapped, _)) = self.niri.layout.find_window_and_output_mut(surface) {\n                mapped.set_needs_configure();\n            } else {\n                toplevel.send_configure();\n            }\n        }\n    }\n}\ndelegate_xdg_decoration!(State);\n\n/// Whether KDE server decorations are in use.\n#[derive(Default, Clone)]\npub struct KdeDecorationsModeState {\n    server: Cell<bool>,\n}\n\nimpl KdeDecorationsModeState {\n    pub fn is_server(&self) -> bool {\n        self.server.get()\n    }\n}\n\nimpl KdeDecorationHandler for State {\n    fn kde_decoration_state(&self) -> &KdeDecorationState {\n        &self.niri.kde_decoration_state\n    }\n\n    fn request_mode(\n        &mut self,\n        surface: &WlSurface,\n        decoration: &org_kde_kwin_server_decoration::OrgKdeKwinServerDecoration,\n        mode: wayland_server::WEnum<org_kde_kwin_server_decoration::Mode>,\n    ) {\n        let WEnum::Value(mode) = mode else {\n            return;\n        };\n\n        decoration.mode(mode);\n\n        with_states(surface, |states| {\n            let state = states\n                .data_map\n                .get_or_insert(KdeDecorationsModeState::default);\n            state\n                .server\n                .set(mode == org_kde_kwin_server_decoration::Mode::Server);\n        });\n    }\n}\ndelegate_kde_decoration!(State);\n\nimpl XdgForeignHandler for State {\n    fn xdg_foreign_state(&mut self) -> &mut XdgForeignState {\n        &mut self.niri.xdg_foreign_state\n    }\n}\ndelegate_xdg_foreign!(State);\n\nimpl State {\n    pub fn send_initial_configure(&mut self, toplevel: &ToplevelSurface) {\n        let _span = tracy_client::span!(\"State::send_initial_configure\");\n\n        let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) else {\n            error!(\"window must be present in unmapped_windows in send_initial_configure()\");\n            return;\n        };\n\n        let config = self.niri.config.borrow();\n        let rules = ResolvedWindowRules::compute(\n            &config.window_rules,\n            WindowRef::Unmapped(unmapped),\n            self.niri.is_at_startup,\n        );\n\n        let Unmapped { window, state, .. } = unmapped;\n\n        let InitialConfigureState::NotConfigured {\n            wants_fullscreen,\n            wants_maximized,\n        } = state\n        else {\n            error!(\"window must not be already configured in send_initial_configure()\");\n            return;\n        };\n\n        // Pick the target monitor. First, check if we had a workspace set in the window rules.\n        let mon = rules\n            .open_on_workspace\n            .as_deref()\n            .and_then(|name| self.niri.layout.monitor_for_workspace(name));\n\n        // If not, check if we had an output set in the window rules.\n        let mon = mon.or_else(|| {\n            rules\n                .open_on_output\n                .as_deref()\n                .and_then(|name| {\n                    self.niri\n                        .global_space\n                        .outputs()\n                        .find(|output| output_matches_name(output, name))\n                })\n                .and_then(|o| self.niri.layout.monitor_for_output(o))\n        });\n\n        // If not, check if the window requested one for fullscreen.\n        let mon = mon.or_else(|| {\n            wants_fullscreen\n                .as_ref()\n                .and_then(|x| x.as_ref())\n                // The monitor might not exist if the output was disconnected.\n                .and_then(|o| self.niri.layout.monitor_for_output(o))\n        });\n\n        // If not, check if this is a dialog with a parent, to place it next to the parent.\n        let mon = mon.map(|mon| (mon, false)).or_else(|| {\n            toplevel\n                .parent()\n                .and_then(|parent| self.niri.layout.find_window_and_output(&parent))\n                .and_then(|(_win, output)| output)\n                .and_then(|o| self.niri.layout.monitor_for_output(o))\n                .map(|mon| (mon, true))\n        });\n\n        // If not, use the active monitor.\n        let mon = mon.or_else(|| {\n            self.niri\n                .layout\n                .active_monitor_ref()\n                .map(|mon| (mon, false))\n        });\n\n        // If we're following the parent, don't set the target output, so that when the window is\n        // mapped, it fetches the possibly changed parent's output again, and shows up there.\n        let output = mon\n            .filter(|(_, parent)| !parent)\n            .map(|(mon, _)| mon.output().clone());\n        let mon = mon.map(|(mon, _)| mon);\n\n        let mut width = None;\n        let mut floating_width = None;\n        let mut height = None;\n        let mut floating_height = None;\n        let is_full_width = rules.open_maximized.unwrap_or(false);\n        let is_floating = rules.compute_open_floating(toplevel);\n\n        // Tell the surface the preferred size and bounds for its likely output.\n        let ws = rules\n            .open_on_workspace\n            .as_deref()\n            .and_then(|name| mon.map(|mon| mon.find_named_workspace(name)))\n            .unwrap_or_else(|| {\n                mon.map(|mon| mon.active_workspace_ref())\n                    .or_else(|| self.niri.layout.active_workspace())\n            });\n\n        let mut is_pending_maximized = false;\n        if let Some(ws) = ws {\n            // Set a fullscreen and maximized state based on window request and window rule.\n            is_pending_maximized = (*wants_maximized && rules.open_maximized_to_edges.is_none())\n                || rules.open_maximized_to_edges == Some(true);\n\n            if (wants_fullscreen.is_some() && rules.open_fullscreen.is_none())\n                || rules.open_fullscreen == Some(true)\n            {\n                toplevel.with_pending_state(|state| {\n                    state.states.set(xdg_toplevel::State::Fullscreen);\n                });\n            } else if is_pending_maximized {\n                toplevel.with_pending_state(|state| {\n                    state.states.set(xdg_toplevel::State::Maximized);\n                });\n            }\n\n            width = ws.resolve_default_width(rules.default_width, false);\n            floating_width = ws.resolve_default_width(rules.default_width, true);\n            height = ws.resolve_default_height(rules.default_height, false);\n            floating_height = ws.resolve_default_height(rules.default_height, true);\n\n            let configure_width = if is_floating {\n                floating_width\n            } else if is_full_width {\n                Some(PresetSize::Proportion(1.))\n            } else {\n                width\n            };\n            let configure_height = if is_floating { floating_height } else { height };\n            ws.configure_new_window(\n                window,\n                configure_width,\n                configure_height,\n                is_floating,\n                &rules,\n            );\n        }\n\n        // Set the tiled state for the initial configure.\n        update_tiled_state(toplevel, config.prefer_no_csd, rules.tiled_state);\n\n        // Set the configured settings.\n        *state = InitialConfigureState::Configured {\n            rules,\n            width,\n            height,\n            floating_width,\n            floating_height,\n            is_full_width,\n            output,\n            workspace_name: ws.and_then(|w| w.name().cloned()),\n            is_pending_maximized,\n        };\n\n        trace!(surface = %toplevel.wl_surface().id(), \"sending initial configure\");\n        toplevel.send_configure();\n    }\n\n    pub fn queue_initial_configure(&self, toplevel: ToplevelSurface) {\n        // Send the initial configure in an idle, in case the client sent some more info after the\n        // initial commit.\n        self.niri.event_loop.insert_idle(move |state| {\n            if !toplevel.alive() {\n                return;\n            }\n\n            if let Some(unmapped) = state.niri.unmapped_windows.get(toplevel.wl_surface()) {\n                if unmapped.needs_initial_configure() {\n                    state.send_initial_configure(&toplevel);\n                }\n            }\n        });\n    }\n\n    /// Should be called on `WlSurface::commit`\n    pub fn popups_handle_commit(&mut self, surface: &WlSurface) {\n        self.niri.popups.commit(surface);\n\n        if let Some(popup) = self.niri.popups.find_popup(surface) {\n            match popup {\n                PopupKind::Xdg(ref popup) => {\n                    if !popup.is_initial_configure_sent() {\n                        if let Some(output) = self.output_for_popup(&PopupKind::Xdg(popup.clone()))\n                        {\n                            let scale = output.current_scale();\n                            let transform = output.current_transform();\n                            with_states(surface, |data| {\n                                send_scale_transform(surface, data, scale, transform);\n                            });\n                        }\n                        popup.send_configure().expect(\"initial configure failed\");\n                    }\n                }\n                // Input method popup can arbitrary change its geometry, so we need to unconstrain\n                // it on commit.\n                PopupKind::InputMethod(_) => {\n                    self.unconstrain_popup(&popup);\n                }\n            }\n        }\n    }\n\n    pub fn output_for_popup(&self, popup: &PopupKind) -> Option<&Output> {\n        let root = find_popup_root_surface(popup).ok()?;\n        self.niri.output_for_root(&root)\n    }\n\n    pub fn unconstrain_popup(&self, popup: &PopupKind) {\n        let _span = tracy_client::span!(\"Niri::unconstrain_popup\");\n\n        // Popups with a NULL parent will get repositioned in their respective protocol handlers\n        // (i.e. layer-shell).\n        let Ok(root) = find_popup_root_surface(popup) else {\n            return;\n        };\n\n        // Figure out if the root is a window or a layer surface.\n        if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&root) {\n            self.unconstrain_window_popup(popup, &mapped.window);\n        } else if let Some((layer_surface, output)) = self.niri.layout.outputs().find_map(|o| {\n            let map = layer_map_for_output(o);\n            let layer_surface = map.layer_for_surface(&root, WindowSurfaceType::TOPLEVEL)?;\n            Some((layer_surface.clone(), o))\n        }) {\n            self.unconstrain_layer_shell_popup(popup, &layer_surface, output);\n        }\n    }\n\n    fn unconstrain_window_popup(&self, popup: &PopupKind, window: &Window) {\n        // The target geometry for the positioner should be relative to its parent's geometry, so\n        // we will compute that here.\n        let mut target = self.niri.layout.popup_target_rect(window);\n        target.loc -= get_popup_toplevel_coords(popup).to_f64();\n\n        self.position_popup_within_rect(popup, target, true);\n    }\n\n    pub fn unconstrain_layer_shell_popup(\n        &self,\n        popup: &PopupKind,\n        layer_surface: &LayerSurface,\n        output: &Output,\n    ) {\n        let output_geo = self.niri.global_space.output_geometry(output).unwrap();\n        let map = layer_map_for_output(output);\n        let Some(layer_geo) = map.layer_geometry(layer_surface) else {\n            return;\n        };\n\n        // The target geometry for the positioner should be relative to its parent's geometry, so\n        // we will compute that here.\n        let mut target = Rectangle::from_size(output_geo.size);\n\n        // Background and bottom layer popups render below the top and the overlay layer, so let's\n        // put them into the non-exclusive zone.\n        //\n        // FIXME: ideally this should use the \"top and overlay layer\" non-exclusive zone, but\n        // Smithay only computes the \"all layers\" non-exclusive zone atm.\n        //\n        // FIXME: related to the above, top layer popups should use the \"overlay layer\"\n        // non-exclusive zone.\n        if matches!(layer_surface.layer(), Layer::Background | Layer::Bottom) {\n            target = map.non_exclusive_zone();\n        }\n\n        target.loc -= layer_geo.loc;\n        target.loc -= get_popup_toplevel_coords(popup);\n\n        // Don't add padding to layer-shell popups. It's not really needed, and it's unexpected.\n        self.position_popup_within_rect(popup, target.to_f64(), false);\n    }\n\n    fn position_popup_within_rect(\n        &self,\n        popup: &PopupKind,\n        target: Rectangle<f64, Logical>,\n        padding: bool,\n    ) {\n        match popup {\n            PopupKind::Xdg(popup) => {\n                popup.with_pending_state(|state| {\n                    state.geometry = if padding {\n                        unconstrain_with_padding(state.positioner, target)\n                    } else {\n                        state\n                            .positioner\n                            .get_unconstrained_geometry(target.to_i32_round())\n                    };\n                });\n            }\n            PopupKind::InputMethod(popup) => {\n                let text_input_rectangle = popup.text_input_rectangle();\n                let mut bbox =\n                    utils::bbox_from_surface_tree(popup.wl_surface(), text_input_rectangle.loc)\n                        .to_f64();\n\n                // Position bbox horizontally first.\n                let overflow_x = (bbox.loc.x + bbox.size.w) - (target.loc.x + target.size.w);\n                if overflow_x > 0. {\n                    bbox.loc.x -= overflow_x;\n                }\n\n                // Ensure that the popup starts within the window.\n                bbox.loc.x = f64::max(bbox.loc.x, target.loc.x);\n\n                // Try to position IME popup below the text input rectangle.\n                let mut below = bbox;\n                below.loc.y += f64::from(text_input_rectangle.size.h);\n\n                let mut above = bbox;\n                above.loc.y -= bbox.size.h;\n\n                if target.loc.y + target.size.h >= below.loc.y + below.size.h {\n                    popup.set_location(below.loc.to_i32_round());\n                } else {\n                    popup.set_location(above.loc.to_i32_round());\n                }\n            }\n        }\n    }\n\n    pub fn update_reactive_popups(&self, window: &Window) {\n        let _span = tracy_client::span!(\"Niri::update_reactive_popups\");\n\n        for (popup, _) in PopupManager::popups_for_surface(\n            window.toplevel().expect(\"no x11 support\").wl_surface(),\n        ) {\n            match &popup {\n                xdg_popup @ PopupKind::Xdg(popup) => {\n                    if popup.with_pending_state(|state| state.positioner.reactive) {\n                        self.unconstrain_window_popup(xdg_popup, window);\n                        if let Err(err) = popup.send_pending_configure() {\n                            warn!(\"error re-configuring reactive popup: {err:?}\");\n                        }\n                    }\n                }\n                PopupKind::InputMethod(_) => (),\n            }\n        }\n    }\n\n    pub fn update_window_rules(&mut self, toplevel: &ToplevelSurface) {\n        let config = self.niri.config.borrow();\n        let window_rules = &config.window_rules;\n\n        if let Some(unmapped) = self.niri.unmapped_windows.get_mut(toplevel.wl_surface()) {\n            let new_rules = ResolvedWindowRules::compute(\n                window_rules,\n                WindowRef::Unmapped(unmapped),\n                self.niri.is_at_startup,\n            );\n            if let InitialConfigureState::Configured { rules, .. } = &mut unmapped.state {\n                *rules = new_rules;\n            }\n        } else if let Some((mapped, output)) = self\n            .niri\n            .layout\n            .find_window_and_output_mut(toplevel.wl_surface())\n        {\n            if mapped.recompute_window_rules(window_rules, self.niri.is_at_startup) {\n                drop(config);\n                let output = output.cloned();\n                let window = mapped.window.clone();\n                self.niri.layout.update_window(&window, None);\n\n                if let Some(output) = output {\n                    self.niri.queue_redraw(&output);\n                }\n            }\n        }\n    }\n}\n\nfn unconstrain_with_padding(\n    positioner: PositionerState,\n    target: Rectangle<f64, Logical>,\n) -> Rectangle<i32, Logical> {\n    // Try unconstraining with a small padding first which looks nicer, then if it doesn't fit try\n    // unconstraining without padding.\n    const PADDING: f64 = 8.;\n\n    let mut padded = target;\n    if PADDING * 2. < padded.size.w {\n        padded.loc.x += PADDING;\n        padded.size.w -= PADDING * 2.;\n    }\n    if PADDING * 2. < padded.size.h {\n        padded.loc.y += PADDING;\n        padded.size.h -= PADDING * 2.;\n    }\n\n    // No padding, so just unconstrain with the original target.\n    if padded == target {\n        return positioner.get_unconstrained_geometry(target.to_i32_round());\n    }\n\n    // Do not try to resize to fit the padded target rectangle.\n    let mut no_resize = positioner;\n    no_resize\n        .constraint_adjustment\n        .remove(ConstraintAdjustment::ResizeX);\n    no_resize\n        .constraint_adjustment\n        .remove(ConstraintAdjustment::ResizeY);\n\n    let geo = no_resize.get_unconstrained_geometry(padded.to_i32_round());\n    if padded.contains_rect(geo.to_f64()) {\n        return geo;\n    }\n\n    // Could not unconstrain into the padded target, so resort to the regular one.\n    positioner.get_unconstrained_geometry(target.to_i32_round())\n}\n\npub fn add_mapped_toplevel_pre_commit_hook(toplevel: &ToplevelSurface) -> HookId {\n    add_pre_commit_hook::<State, _>(toplevel.wl_surface(), move |state, _dh, surface| {\n        let _span = tracy_client::span!(\"mapped toplevel pre-commit\");\n        let span =\n            trace_span!(\"toplevel pre-commit\", surface = %surface.id(), serial = Empty).entered();\n\n        let Some((mapped, _)) = state.niri.layout.find_window_and_output_mut(surface) else {\n            error!(\"pre-commit hook for mapped surfaces must be removed upon unmapping\");\n            return;\n        };\n\n        let (got_unmapped, dmabuf, commit_serial) = with_states(surface, |states| {\n            let (got_unmapped, dmabuf) = {\n                let mut guard = states.cached_state.get::<SurfaceAttributes>();\n                match guard.pending().buffer.as_ref() {\n                    Some(BufferAssignment::NewBuffer(buffer)) => {\n                        let dmabuf = get_dmabuf(buffer).cloned().ok();\n                        (false, dmabuf)\n                    }\n                    Some(BufferAssignment::Removed) => (true, None),\n                    None => (false, None),\n                }\n            };\n\n            let role = states\n                .data_map\n                .get::<XdgToplevelSurfaceData>()\n                .unwrap()\n                .lock()\n                .unwrap();\n            let serial = role.last_acked.as_ref().map(|c| c.serial);\n\n            (got_unmapped, dmabuf, serial)\n        });\n\n        let mut transaction_for_dmabuf = None;\n        let mut animate = false;\n        if let Some(serial) = commit_serial {\n            if !span.is_disabled() {\n                span.record(\"serial\", format!(\"{serial:?}\"));\n            }\n\n            // trace!(\"taking pending transaction\");\n            if let Some(transaction) = mapped.take_pending_transaction(serial) {\n                // Transaction can be already completed if it ran past the deadline.\n                let disable = state.niri.config.borrow().debug.disable_transactions;\n                if !transaction.is_completed() && !disable {\n                    // Register the deadline even if this is the last pending, since dmabuf\n                    // rendering can still run over the deadline.\n                    transaction.register_deadline_timer(&state.niri.event_loop);\n\n                    let is_last = transaction.is_last();\n\n                    // If this is the last transaction, we don't need to add a separate\n                    // notification, because the transaction will complete in our dmabuf blocker\n                    // callback, which already calls blocker_cleared(), or by the end of this\n                    // function, in which case there would be no blocker in the first place.\n                    if !is_last {\n                        // Waiting for some other surface; register a notification and add a\n                        // transaction blocker.\n                        if let Some(client) = surface.client() {\n                            transaction.add_notification(\n                                state.niri.blocker_cleared_tx.clone(),\n                                client.clone(),\n                            );\n                            add_blocker(surface, transaction.blocker());\n                        }\n                    }\n\n                    // Delay dropping (and completing) the transaction until the dmabuf is ready.\n                    // If there's no dmabuf, this will be dropped by the end of this pre-commit\n                    // hook.\n                    transaction_for_dmabuf = Some(transaction);\n                }\n            }\n\n            animate = mapped.should_animate_commit(serial);\n        } else if !got_unmapped {\n            error!(\"commit on a mapped surface without a configured serial\");\n        };\n\n        if let Some((blocker, source)) =\n            dmabuf.and_then(|dmabuf| dmabuf.generate_blocker(Interest::READ).ok())\n        {\n            if let Some(client) = surface.client() {\n                let res = state\n                    .niri\n                    .event_loop\n                    .insert_source(source, move |_, _, state| {\n                        // This surface is now ready for the transaction.\n                        drop(transaction_for_dmabuf.take());\n\n                        let display_handle = state.niri.display_handle.clone();\n                        state\n                            .client_compositor_state(&client)\n                            .blocker_cleared(state, &display_handle);\n\n                        Ok(())\n                    });\n                if res.is_ok() {\n                    add_blocker(surface, blocker);\n                    trace!(\"added dmabuf blocker\");\n                }\n            }\n        }\n\n        let window = mapped.window.clone();\n        if got_unmapped {\n            state.backend.with_primary_renderer(|renderer| {\n                state.niri.layout.store_unmap_snapshot(renderer, &window);\n            });\n        } else {\n            if animate {\n                state.backend.with_primary_renderer(|renderer| {\n                    mapped.store_animation_snapshot(renderer);\n                });\n            }\n\n            // The toplevel remains mapped; clear any stored unmap snapshot.\n            state.niri.layout.clear_unmap_snapshot(&window);\n        }\n    })\n}\n"
  },
  {
    "path": "src/input/backend_ext.rs",
    "content": "use ::input as libinput;\nuse smithay::backend::input;\nuse smithay::backend::winit::WinitVirtualDevice;\nuse smithay::output::Output;\n\nuse crate::niri::State;\nuse crate::protocols::virtual_pointer::VirtualPointer;\n\npub trait NiriInputBackend: input::InputBackend<Device = Self::NiriDevice> {\n    type NiriDevice: NiriInputDevice;\n}\nimpl<T: input::InputBackend> NiriInputBackend for T\nwhere\n    Self::Device: NiriInputDevice,\n{\n    type NiriDevice = Self::Device;\n}\n\npub trait NiriInputDevice: input::Device {\n    // FIXME: this should maybe be per-event, not per-device,\n    // but it's not clear that this matters in practice?\n    // it might be more obvious once we implement it for libinput\n    fn output(&self, state: &State) -> Option<Output>;\n}\n\nimpl NiriInputDevice for libinput::Device {\n    fn output(&self, _state: &State) -> Option<Output> {\n        // FIXME: Allow specifying the output per-device?\n        None\n    }\n}\n\nimpl NiriInputDevice for WinitVirtualDevice {\n    fn output(&self, _state: &State) -> Option<Output> {\n        // FIXME: we should be returning the single output that the winit backend creates,\n        // but for now, that will cause issues because the output is normally upside down,\n        // so we apply Transform::Flipped180 to it and that would also cause\n        // the cursor position to be flipped, which is not what we want.\n        //\n        // instead, we just return None and rely on the fact that it has only one output.\n        // doing so causes the cursor to be placed in *global* output coordinates,\n        // which are not flipped, and happen to be what we want.\n        None\n    }\n}\n\nimpl NiriInputDevice for VirtualPointer {\n    fn output(&self, _: &State) -> Option<Output> {\n        self.output().cloned()\n    }\n}\n"
  },
  {
    "path": "src/input/mod.rs",
    "content": "use std::any::Any;\nuse std::cmp::min;\nuse std::collections::hash_map::Entry;\nuse std::collections::HashSet;\nuse std::time::Duration;\n\nuse calloop::timer::{TimeoutAction, Timer};\nuse input::event::gesture::GestureEventCoordinates as _;\nuse niri_config::{\n    Action, Bind, Binds, Config, Key, ModKey, Modifiers, MruDirection, SwitchBinds, Trigger,\n};\nuse niri_ipc::LayoutSwitchTarget;\nuse smithay::backend::input::{\n    AbsolutePositionEvent, Axis, AxisSource, ButtonState, Device, DeviceCapability, Event,\n    GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _,\n    InputEvent, KeyState, KeyboardKeyEvent, Keycode, MouseButton, PointerAxisEvent,\n    PointerButtonEvent, PointerMotionEvent, ProximityState, Switch, SwitchState, SwitchToggleEvent,\n    TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent,\n    TabletToolTipState, TouchEvent,\n};\nuse smithay::backend::libinput::LibinputInputBackend;\nuse smithay::input::dnd::DnDGrab;\nuse smithay::input::keyboard::{keysyms, FilterResult, Keysym, Layout, ModifiersState};\nuse smithay::input::pointer::{\n    AxisFrame, ButtonEvent, CursorIcon, CursorImageStatus, Focus, GestureHoldBeginEvent,\n    GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,\n    GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,\n    GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, RelativeMotionEvent,\n};\nuse smithay::input::touch::{\n    DownEvent, GrabStartData as TouchGrabStartData, MotionEvent as TouchMotionEvent, UpEvent,\n};\nuse smithay::input::SeatHandler;\nuse smithay::output::Output;\nuse smithay::reexports::wayland_server::protocol::wl_data_source::WlDataSource;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::utils::{Logical, Point, Rectangle, Transform, SERIAL_COUNTER};\nuse smithay::wayland::keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitor;\nuse smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};\nuse smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait};\nuse touch_overview_grab::TouchOverviewGrab;\n\nuse self::move_grab::MoveGrab;\nuse self::resize_grab::ResizeGrab;\nuse self::spatial_movement_grab::SpatialMovementGrab;\n#[cfg(feature = \"dbus\")]\nuse crate::dbus::freedesktop_a11y::KbMonBlock;\nuse crate::layout::scrolling::ScrollDirection;\nuse crate::layout::{ActivateWindow, LayoutElement as _};\nuse crate::niri::{CastTarget, PointerVisibility, State};\nuse crate::ui::mru::{WindowMru, WindowMruUi};\nuse crate::ui::screenshot_ui::ScreenshotUi;\nuse crate::utils::spawning::{spawn, spawn_sh};\nuse crate::utils::{center, get_monotonic_time, CastSessionId, ResizeEdge};\n\npub mod backend_ext;\npub mod move_grab;\npub mod pick_color_grab;\npub mod pick_window_grab;\npub mod resize_grab;\npub mod scroll_swipe_gesture;\npub mod scroll_tracker;\npub mod spatial_movement_grab;\npub mod swipe_tracker;\npub mod touch_overview_grab;\npub mod touch_resize_grab;\n\nuse backend_ext::{NiriInputBackend as InputBackend, NiriInputDevice as _};\n\npub const DOUBLE_CLICK_TIME: Duration = Duration::from_millis(400);\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub struct TabletData {\n    pub aspect_ratio: f64,\n}\n\npub enum PointerOrTouchStartData<D: SeatHandler> {\n    Pointer(PointerGrabStartData<D>),\n    Touch(TouchGrabStartData<D>),\n}\n\nimpl<D: SeatHandler> PointerOrTouchStartData<D> {\n    pub fn location(&self) -> Point<f64, Logical> {\n        match self {\n            PointerOrTouchStartData::Pointer(x) => x.location,\n            PointerOrTouchStartData::Touch(x) => x.location,\n        }\n    }\n\n    pub fn unwrap_pointer(&self) -> &PointerGrabStartData<D> {\n        match self {\n            PointerOrTouchStartData::Pointer(x) => x,\n            PointerOrTouchStartData::Touch(_) => panic!(\"start_data is not Pointer\"),\n        }\n    }\n\n    pub fn unwrap_touch(&self) -> &TouchGrabStartData<D> {\n        match self {\n            PointerOrTouchStartData::Pointer(_) => panic!(\"start_data is not Touch\"),\n            PointerOrTouchStartData::Touch(x) => x,\n        }\n    }\n\n    pub fn is_pointer(&self) -> bool {\n        matches!(self, Self::Pointer(_))\n    }\n\n    pub fn is_touch(&self) -> bool {\n        matches!(self, Self::Touch(_))\n    }\n}\n\nimpl State {\n    pub fn process_input_event<I: InputBackend + 'static>(&mut self, event: InputEvent<I>)\n    where\n        I::Device: 'static, // Needed for downcasting.\n    {\n        let _span = tracy_client::span!(\"process_input_event\");\n\n        // Make sure some logic like workspace clean-up has a chance to run before doing actions.\n        self.niri.advance_animations();\n\n        if self.niri.monitors_active {\n            // Notify the idle-notifier of activity.\n            if should_notify_activity(&event) {\n                self.niri.notify_activity();\n            }\n        } else {\n            // Power on monitors if they were off.\n            if should_activate_monitors(&event) {\n                self.niri.activate_monitors(&mut self.backend);\n\n                // Notify the idle-notifier of activity only if we're also powering on the\n                // monitors.\n                self.niri.notify_activity();\n            }\n        }\n\n        if should_reset_pointer_inactivity_timer(&event) {\n            self.niri.reset_pointer_inactivity_timer();\n        }\n\n        let hide_hotkey_overlay =\n            self.niri.hotkey_overlay.is_open() && should_hide_hotkey_overlay(&event);\n\n        let hide_exit_confirm_dialog =\n            self.niri.exit_confirm_dialog.is_open() && should_hide_exit_confirm_dialog(&event);\n\n        let mut consumed_by_a11y = false;\n        use InputEvent::*;\n        match event {\n            DeviceAdded { device } => self.on_device_added(device),\n            DeviceRemoved { device } => self.on_device_removed(device),\n            Keyboard { event } => self.on_keyboard::<I>(event, &mut consumed_by_a11y),\n            PointerMotion { event } => self.on_pointer_motion::<I>(event),\n            PointerMotionAbsolute { event } => self.on_pointer_motion_absolute::<I>(event),\n            PointerButton { event } => self.on_pointer_button::<I>(event),\n            PointerAxis { event } => self.on_pointer_axis::<I>(event),\n            TabletToolAxis { event } => self.on_tablet_tool_axis::<I>(event),\n            TabletToolTip { event } => self.on_tablet_tool_tip::<I>(event),\n            TabletToolProximity { event } => self.on_tablet_tool_proximity::<I>(event),\n            TabletToolButton { event } => self.on_tablet_tool_button::<I>(event),\n            GestureSwipeBegin { event } => self.on_gesture_swipe_begin::<I>(event),\n            GestureSwipeUpdate { event } => self.on_gesture_swipe_update::<I>(event),\n            GestureSwipeEnd { event } => self.on_gesture_swipe_end::<I>(event),\n            GesturePinchBegin { event } => self.on_gesture_pinch_begin::<I>(event),\n            GesturePinchUpdate { event } => self.on_gesture_pinch_update::<I>(event),\n            GesturePinchEnd { event } => self.on_gesture_pinch_end::<I>(event),\n            GestureHoldBegin { event } => self.on_gesture_hold_begin::<I>(event),\n            GestureHoldEnd { event } => self.on_gesture_hold_end::<I>(event),\n            TouchDown { event } => self.on_touch_down::<I>(event),\n            TouchMotion { event } => self.on_touch_motion::<I>(event),\n            TouchUp { event } => self.on_touch_up::<I>(event),\n            TouchCancel { event } => self.on_touch_cancel::<I>(event),\n            TouchFrame { event } => self.on_touch_frame::<I>(event),\n            SwitchToggle { event } => self.on_switch_toggle::<I>(event),\n            Special(_) => (),\n        }\n\n        // Don't hide overlays if consumed by a11y, so that you can use the screen reader\n        // navigation keys.\n        if consumed_by_a11y {\n            return;\n        }\n\n        // Do this last so that screenshot still gets it.\n        if hide_hotkey_overlay && self.niri.hotkey_overlay.hide() {\n            self.niri.queue_redraw_all();\n        }\n\n        if hide_exit_confirm_dialog && self.niri.exit_confirm_dialog.hide() {\n            self.niri.queue_redraw_all();\n        }\n    }\n\n    pub fn process_libinput_event(&mut self, event: &mut InputEvent<LibinputInputBackend>) {\n        let _span = tracy_client::span!(\"process_libinput_event\");\n\n        match event {\n            InputEvent::DeviceAdded { device } => {\n                self.niri.devices.insert(device.clone());\n\n                if device.has_capability(input::DeviceCapability::TabletTool) {\n                    match device.size() {\n                        Some((w, h)) => {\n                            let aspect_ratio = w / h;\n                            let data = TabletData { aspect_ratio };\n                            self.niri.tablets.insert(device.clone(), data);\n                        }\n                        None => {\n                            warn!(\"tablet tool device has no size\");\n                        }\n                    }\n                }\n\n                if device.has_capability(input::DeviceCapability::Keyboard) {\n                    if let Some(led_state) = self\n                        .niri\n                        .seat\n                        .get_keyboard()\n                        .map(|keyboard| keyboard.led_state())\n                    {\n                        device.led_update(led_state.into());\n                    }\n                }\n\n                if device.has_capability(input::DeviceCapability::Touch) {\n                    self.niri.touch.insert(device.clone());\n                }\n\n                apply_libinput_settings(&self.niri.config.borrow().input, device);\n            }\n            InputEvent::DeviceRemoved { device } => {\n                self.niri.touch.remove(device);\n                self.niri.tablets.remove(device);\n                self.niri.devices.remove(device);\n            }\n            _ => (),\n        }\n    }\n\n    fn on_device_added(&mut self, device: impl Device) {\n        if device.has_capability(DeviceCapability::TabletTool) {\n            let tablet_seat = self.niri.seat.tablet_seat();\n\n            let desc = TabletDescriptor::from(&device);\n            tablet_seat.add_tablet::<Self>(&self.niri.display_handle, &desc);\n        }\n        if device.has_capability(DeviceCapability::Touch) && self.niri.seat.get_touch().is_none() {\n            self.niri.seat.add_touch();\n        }\n    }\n\n    fn on_device_removed(&mut self, device: impl Device) {\n        if device.has_capability(DeviceCapability::TabletTool) {\n            let tablet_seat = self.niri.seat.tablet_seat();\n\n            let desc = TabletDescriptor::from(&device);\n            tablet_seat.remove_tablet(&desc);\n\n            // If there are no tablets in seat we can remove all tools\n            if tablet_seat.count_tablets() == 0 {\n                tablet_seat.clear_tools();\n            }\n        }\n        if device.has_capability(DeviceCapability::Touch) && self.niri.touch.is_empty() {\n            self.niri.seat.remove_touch();\n        }\n    }\n\n    /// Computes the rectangle that covers all outputs in global space.\n    fn global_bounding_rectangle(&self) -> Option<Rectangle<i32, Logical>> {\n        self.niri.global_space.outputs().fold(\n            None,\n            |acc: Option<Rectangle<i32, Logical>>, output| {\n                self.niri\n                    .global_space\n                    .output_geometry(output)\n                    .map(|geo| acc.map(|acc| acc.merge(geo)).unwrap_or(geo))\n            },\n        )\n    }\n\n    /// Computes the cursor position for the tablet event.\n    ///\n    /// This function handles the tablet output mapping, as well as coordinate clamping and aspect\n    /// ratio correction.\n    fn compute_tablet_position<I: InputBackend>(\n        &self,\n        event: &(impl Event<I> + TabletToolEvent<I>),\n    ) -> Option<Point<f64, Logical>>\n    where\n        I::Device: 'static,\n    {\n        let device_output = event.device().output(self);\n        let device_output = device_output.as_ref();\n        let (target_geo, keep_ratio, px, transform) =\n            if let Some(output) = device_output.or_else(|| self.niri.output_for_tablet()) {\n                (\n                    self.niri.global_space.output_geometry(output).unwrap(),\n                    true,\n                    1. / output.current_scale().fractional_scale(),\n                    output.current_transform(),\n                )\n            } else {\n                let geo = self.global_bounding_rectangle()?;\n\n                // FIXME: this 1 px size should ideally somehow be computed for the rightmost output\n                // corresponding to the position on the right when clamping.\n                let output = self.niri.global_space.outputs().next().unwrap();\n                let scale = output.current_scale().fractional_scale();\n\n                // Do not keep ratio for the unified mode as this is what OpenTabletDriver expects.\n                (geo, false, 1. / scale, Transform::Normal)\n            };\n\n        let mut pos = {\n            let size = transform.invert().transform_size(target_geo.size);\n            transform.transform_point_in(event.position_transformed(size), &size.to_f64())\n        };\n\n        if keep_ratio {\n            pos.x /= target_geo.size.w as f64;\n            pos.y /= target_geo.size.h as f64;\n\n            let device = event.device();\n            if let Some(device) = (&device as &dyn Any).downcast_ref::<input::Device>() {\n                if let Some(data) = self.niri.tablets.get(device) {\n                    // This code does the same thing as mutter with \"keep aspect ratio\" enabled.\n                    let size = transform.invert().transform_size(target_geo.size);\n                    let output_aspect_ratio = size.w as f64 / size.h as f64;\n                    let ratio = data.aspect_ratio / output_aspect_ratio;\n\n                    if ratio > 1. {\n                        pos.x *= ratio;\n                    } else {\n                        pos.y /= ratio;\n                    }\n                }\n            };\n\n            pos.x *= target_geo.size.w as f64;\n            pos.y *= target_geo.size.h as f64;\n        }\n\n        pos.x = pos.x.clamp(0.0, target_geo.size.w as f64 - px);\n        pos.y = pos.y.clamp(0.0, target_geo.size.h as f64 - px);\n        Some(pos + target_geo.loc.to_f64())\n    }\n\n    fn is_inhibiting_shortcuts(&self) -> bool {\n        self.niri\n            .keyboard_focus\n            .surface()\n            .and_then(|surface| {\n                self.niri\n                    .keyboard_shortcuts_inhibiting_surfaces\n                    .get(surface)\n            })\n            .is_some_and(KeyboardShortcutsInhibitor::is_active)\n    }\n\n    fn on_keyboard<I: InputBackend>(\n        &mut self,\n        event: I::KeyboardKeyEvent,\n        consumed_by_a11y: &mut bool,\n    ) {\n        let mod_key = self.backend.mod_key(&self.niri.config.borrow());\n\n        let serial = SERIAL_COUNTER.next_serial();\n        let time = Event::time_msec(&event);\n        let pressed = event.state() == KeyState::Pressed;\n\n        // Stop bind key repeat on any release. This won't work 100% correctly in cases like:\n        // 1. Press Mod\n        // 2. Press Left (repeat starts)\n        // 3. Press PgDown (new repeat starts)\n        // 4. Release Left (PgDown repeat stops)\n        // But it's good enough for now.\n        // FIXME: handle this properly.\n        if !pressed {\n            if let Some(token) = self.niri.bind_repeat_timer.take() {\n                self.niri.event_loop.remove(token);\n            }\n        }\n\n        if pressed {\n            self.hide_cursor_if_needed();\n        }\n\n        let is_inhibiting_shortcuts = self.is_inhibiting_shortcuts();\n\n        // Accessibility modifier grabs should override XKB state changes (e.g. Caps Lock), so we\n        // need to process them before keyboard.input() below.\n        //\n        // Other accessibility-grabbed keys should still update our XKB state, but not cause any\n        // other changes.\n        #[cfg(feature = \"dbus\")]\n        let block = {\n            let block = self.a11y_process_key(\n                Duration::from_millis(u64::from(time)),\n                event.key_code(),\n                event.state(),\n            );\n            if block != KbMonBlock::Pass {\n                *consumed_by_a11y = true;\n            }\n            // The accessibility modifier first press must not change XKB state, so we return\n            // early here.\n            if block == KbMonBlock::ModifierFirstPress {\n                return;\n            }\n            block\n        };\n        #[cfg(not(feature = \"dbus\"))]\n        let _ = consumed_by_a11y;\n\n        let Some(Some(bind)) = self.niri.seat.get_keyboard().unwrap().input(\n            self,\n            event.key_code(),\n            event.state(),\n            serial,\n            time,\n            |this, mods, keysym| {\n                let key_code = event.key_code();\n                let modified = keysym.modified_sym();\n                let raw = keysym.raw_latin_sym_or_raw_current_sym();\n                let modifiers = modifiers_from_state(*mods);\n\n                // After updating XKB state from accessibility-grabbed keys, return right away and\n                // don't handle them.\n                #[cfg(feature = \"dbus\")]\n                if block != KbMonBlock::Pass {\n                    // HACK: there's a slight problem with this code. Here we filter out keys\n                    // consumed by accessibility from getting sent to the Wayland client. However,\n                    // the Wayland client can still receive these keys from the wl_keyboard\n                    // enter/modifiers events. In particular, this can easily happen when opening\n                    // the Orca actions menu with Orca + Shift + A: in most cases, when this menu\n                    // opens, Shift is still held down, so the menu receives it in\n                    // wl_keyboard.enter/modifiers. Then the menu won't react to Enter presses\n                    // until the user taps Shift again to \"release\" it (since the initial Shift\n                    // release will be intercepted here).\n                    //\n                    // I don't think there's any good way of dealing with this apart from keeping a\n                    // separate xkb state for accessibility, so that we can track the pressed\n                    // modifiers without accidentally leaking them to wl_keyboard.enter. So for now\n                    // let's forward modifier releases to the clients here to deal with the most\n                    // common case.\n                    if !pressed\n                        && matches!(\n                            modified,\n                            Keysym::Shift_L\n                                | Keysym::Shift_R\n                                | Keysym::Control_L\n                                | Keysym::Control_R\n                                | Keysym::Super_L\n                                | Keysym::Super_R\n                                | Keysym::Alt_L\n                                | Keysym::Alt_R\n                        )\n                    {\n                        return FilterResult::Forward;\n                    } else {\n                        return FilterResult::Intercept(None);\n                    }\n                }\n\n                if this.niri.exit_confirm_dialog.is_open() && pressed {\n                    if raw == Some(Keysym::Return) {\n                        info!(\"quitting after confirming exit dialog\");\n                        this.niri.stop_signal.stop();\n                    }\n\n                    // Don't send this press to any clients.\n                    this.niri.suppressed_keys.insert(key_code);\n                    return FilterResult::Intercept(None);\n                }\n\n                // Check if all modifiers were released while the MRU UI was open. If so, close the\n                // UI (which will also transfer the focus to the current MRU UI selection).\n                if this.niri.window_mru_ui.is_open() && !pressed && modifiers.is_empty() {\n                    this.do_action(Action::MruConfirm, false);\n\n                    if this.niri.suppressed_keys.remove(&key_code) {\n                        return FilterResult::Intercept(None);\n                    } else {\n                        return FilterResult::Forward;\n                    }\n                }\n\n                if pressed\n                    && raw == Some(Keysym::Escape)\n                    && (this.niri.pick_window.is_some() || this.niri.pick_color.is_some())\n                {\n                    // We window picking state so the pick window grab must be active.\n                    // Unsetting it cancels window picking.\n                    this.niri\n                        .seat\n                        .get_pointer()\n                        .unwrap()\n                        .unset_grab(this, serial, time);\n                    this.niri.suppressed_keys.insert(key_code);\n                    return FilterResult::Intercept(None);\n                }\n\n                if let Some(Keysym::space) = raw {\n                    this.niri.screenshot_ui.set_space_down(pressed);\n                }\n\n                let res = {\n                    let config = this.niri.config.borrow();\n                    let bindings =\n                        make_binds_iter(&config, &mut this.niri.window_mru_ui, modifiers);\n\n                    should_intercept_key(\n                        &mut this.niri.suppressed_keys,\n                        bindings,\n                        mod_key,\n                        key_code,\n                        modified,\n                        raw,\n                        pressed,\n                        *mods,\n                        &this.niri.screenshot_ui,\n                        this.niri.config.borrow().input.disable_power_key_handling,\n                        is_inhibiting_shortcuts,\n                    )\n                };\n\n                if matches!(res, FilterResult::Forward) {\n                    // If we didn't find any bind, try other hardcoded keys.\n                    if this.niri.keyboard_focus.is_overview() && pressed {\n                        if let Some(bind) = raw.and_then(|raw| hardcoded_overview_bind(raw, *mods))\n                        {\n                            this.niri.suppressed_keys.insert(key_code);\n                            return FilterResult::Intercept(Some(bind));\n                        }\n                    }\n\n                    // Interaction with the active window, immediately update the active window's\n                    // focus timestamp without waiting for a possible pending MRU lock-in delay.\n                    this.niri.mru_apply_keyboard_commit();\n                }\n\n                res\n            },\n        ) else {\n            return;\n        };\n\n        if !pressed {\n            return;\n        }\n\n        self.handle_bind(bind.clone());\n\n        self.start_key_repeat(bind);\n    }\n\n    fn start_key_repeat(&mut self, bind: Bind) {\n        if !bind.repeat {\n            return;\n        }\n\n        // Stop the previous key repeat if any.\n        if let Some(token) = self.niri.bind_repeat_timer.take() {\n            self.niri.event_loop.remove(token);\n        }\n\n        let config = self.niri.config.borrow();\n        let config = &config.input.keyboard;\n\n        let repeat_rate = config.repeat_rate;\n        if repeat_rate == 0 {\n            return;\n        }\n        let repeat_duration = Duration::from_secs_f64(1. / f64::from(repeat_rate));\n\n        let repeat_timer =\n            Timer::from_duration(Duration::from_millis(u64::from(config.repeat_delay)));\n\n        let token = self\n            .niri\n            .event_loop\n            .insert_source(repeat_timer, move |_, _, state| {\n                state.handle_bind(bind.clone());\n                TimeoutAction::ToDuration(repeat_duration)\n            })\n            .unwrap();\n\n        self.niri.bind_repeat_timer = Some(token);\n    }\n\n    fn hide_cursor_if_needed(&mut self) {\n        // If the pointer is already invisible, don't reset it back to Hidden causing one frame\n        // of hover.\n        if !self.niri.pointer_visibility.is_visible() {\n            return;\n        }\n\n        if !self.niri.config.borrow().cursor.hide_when_typing {\n            return;\n        }\n\n        // niri keeps this set only while actively using a tablet, which means the cursor position\n        // is likely to change almost immediately, causing pointer_visibility to just flicker back\n        // and forth.\n        if self.niri.tablet_cursor_location.is_some() {\n            return;\n        }\n\n        self.niri.pointer_visibility = PointerVisibility::Hidden;\n        self.niri.queue_redraw_all();\n    }\n\n    pub fn handle_bind(&mut self, bind: Bind) {\n        let Some(cooldown) = bind.cooldown else {\n            self.do_action(bind.action, bind.allow_when_locked);\n            return;\n        };\n\n        // Check this first so that it doesn't trigger the cooldown.\n        if self.niri.is_locked() && !(bind.allow_when_locked || allowed_when_locked(&bind.action)) {\n            return;\n        }\n\n        match self.niri.bind_cooldown_timers.entry(bind.key) {\n            // The bind is on cooldown.\n            Entry::Occupied(_) => (),\n            Entry::Vacant(entry) => {\n                let timer = Timer::from_duration(cooldown);\n                let token = self\n                    .niri\n                    .event_loop\n                    .insert_source(timer, move |_, _, state| {\n                        if state.niri.bind_cooldown_timers.remove(&bind.key).is_none() {\n                            error!(\"bind cooldown timer entry disappeared\");\n                        }\n                        TimeoutAction::Drop\n                    })\n                    .unwrap();\n                entry.insert(token);\n\n                self.do_action(bind.action, bind.allow_when_locked);\n            }\n        }\n    }\n\n    pub fn do_action(&mut self, action: Action, allow_when_locked: bool) {\n        if self.niri.is_locked() && !(allow_when_locked || allowed_when_locked(&action)) {\n            return;\n        }\n\n        if let Some(touch) = self.niri.seat.get_touch() {\n            touch.cancel(self);\n        }\n\n        match action {\n            Action::Quit(skip_confirmation) => {\n                if !skip_confirmation && self.niri.exit_confirm_dialog.show() {\n                    self.niri.queue_redraw_all();\n                    return;\n                }\n\n                info!(\"quitting as requested\");\n                self.niri.stop_signal.stop()\n            }\n            Action::ChangeVt(vt) => {\n                self.backend.change_vt(vt);\n                // Changing VT may not deliver the key releases, so clear the state.\n                self.niri.suppressed_keys.clear();\n            }\n            Action::Suspend => {\n                self.backend.suspend();\n                // Suspend may not deliver the key releases, so clear the state.\n                self.niri.suppressed_keys.clear();\n            }\n            Action::PowerOffMonitors => {\n                self.niri.deactivate_monitors(&mut self.backend);\n            }\n            Action::PowerOnMonitors => {\n                self.niri.activate_monitors(&mut self.backend);\n            }\n            Action::ToggleDebugTint => {\n                self.backend.toggle_debug_tint();\n                self.niri.queue_redraw_all();\n            }\n            Action::DebugToggleOpaqueRegions => {\n                self.niri.debug_draw_opaque_regions = !self.niri.debug_draw_opaque_regions;\n                self.niri.queue_redraw_all();\n            }\n            Action::DebugToggleDamage => {\n                self.niri.debug_toggle_damage();\n            }\n            Action::Spawn(command) => {\n                let (token, _) = self.niri.activation_state.create_external_token(None);\n                spawn(command, Some(token.clone()));\n            }\n            Action::SpawnSh(command) => {\n                let (token, _) = self.niri.activation_state.create_external_token(None);\n                spawn_sh(command, Some(token.clone()));\n            }\n            Action::DoScreenTransition(delay_ms) => {\n                self.backend.with_primary_renderer(|renderer| {\n                    self.niri.do_screen_transition(renderer, delay_ms);\n                });\n            }\n            Action::ScreenshotScreen(write_to_disk, show_pointer, path) => {\n                let active = self.niri.layout.active_output().cloned();\n                if let Some(active) = active {\n                    self.backend.with_primary_renderer(|renderer| {\n                        if let Err(err) = self.niri.screenshot(\n                            renderer,\n                            &active,\n                            write_to_disk,\n                            show_pointer,\n                            path,\n                        ) {\n                            warn!(\"error taking screenshot: {err:?}\");\n                        }\n                    });\n                }\n            }\n            Action::ConfirmScreenshot { write_to_disk } => {\n                self.confirm_screenshot(write_to_disk);\n            }\n            Action::CancelScreenshot => {\n                if !self.niri.screenshot_ui.is_open() {\n                    return;\n                }\n\n                self.niri.screenshot_ui.close();\n                self.niri\n                    .cursor_manager\n                    .set_cursor_image(CursorImageStatus::default_named());\n                self.niri.queue_redraw_all();\n            }\n            Action::ScreenshotTogglePointer => {\n                self.niri.screenshot_ui.toggle_pointer();\n                self.niri.queue_redraw_all();\n            }\n            Action::Screenshot(show_cursor, path) => {\n                self.open_screenshot_ui(show_cursor, path);\n                self.niri.cancel_mru();\n            }\n            Action::ScreenshotWindow(write_to_disk, show_pointer, path) => {\n                let focus = self.niri.layout.focus_with_output();\n                if let Some((mapped, output)) = focus {\n                    self.backend.with_primary_renderer(|renderer| {\n                        if let Err(err) = self.niri.screenshot_window(\n                            renderer,\n                            output,\n                            mapped,\n                            write_to_disk,\n                            show_pointer,\n                            path,\n                        ) {\n                            warn!(\"error taking screenshot: {err:?}\");\n                        }\n                    });\n                }\n            }\n            Action::ScreenshotWindowById {\n                id,\n                write_to_disk,\n                show_pointer,\n                path,\n            } => {\n                let mut windows = self.niri.layout.windows();\n                let window = windows.find(|(_, m)| m.id().get() == id);\n                if let Some((Some(monitor), mapped)) = window {\n                    let output = monitor.output();\n                    self.backend.with_primary_renderer(|renderer| {\n                        if let Err(err) = self.niri.screenshot_window(\n                            renderer,\n                            output,\n                            mapped,\n                            write_to_disk,\n                            show_pointer,\n                            path,\n                        ) {\n                            warn!(\"error taking screenshot: {err:?}\");\n                        }\n                    });\n                }\n            }\n            Action::ToggleKeyboardShortcutsInhibit => {\n                if let Some(inhibitor) = self.niri.keyboard_focus.surface().and_then(|surface| {\n                    self.niri\n                        .keyboard_shortcuts_inhibiting_surfaces\n                        .get(surface)\n                }) {\n                    if inhibitor.is_active() {\n                        inhibitor.inactivate();\n                    } else {\n                        inhibitor.activate();\n                    }\n                }\n            }\n            Action::CloseWindow => {\n                if let Some(mapped) = self.niri.layout.focus() {\n                    mapped.toplevel().send_close();\n                }\n            }\n            Action::CloseWindowById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                if let Some((_, mapped)) = window {\n                    mapped.toplevel().send_close();\n                }\n            }\n            Action::FullscreenWindow => {\n                let focus = self.niri.layout.focus().map(|m| m.window.clone());\n                if let Some(window) = focus {\n                    self.niri.layout.toggle_fullscreen(&window);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::FullscreenWindowById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_fullscreen(&window);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::ToggleWindowedFullscreen => {\n                let focus = self.niri.layout.focus().map(|m| m.window.clone());\n                if let Some(window) = focus {\n                    self.niri.layout.toggle_windowed_fullscreen(&window);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::ToggleWindowedFullscreenById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_windowed_fullscreen(&window);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::FocusWindow(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.focus_window(&window);\n                }\n            }\n            Action::FocusWindowInColumn(index) => {\n                self.niri.layout.focus_window_in_column(index);\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowPrevious => {\n                let current = self.niri.layout.focus().map(|win| win.id());\n                if let Some(window) = self\n                    .niri\n                    .layout\n                    .windows()\n                    .map(|(_, win)| win)\n                    .filter(|win| Some(win.id()) != current)\n                    .max_by_key(|win| win.get_focus_timestamp())\n                    .map(|win| win.window.clone())\n                {\n                    // Commit current focus so repeated focus-window-previous works as expected.\n                    self.niri.mru_apply_keyboard_commit();\n\n                    self.focus_window(&window);\n                }\n            }\n            Action::SwitchLayout(action) => {\n                let keyboard = &self.niri.seat.get_keyboard().unwrap();\n                keyboard.with_xkb_state(self, |mut state| match action {\n                    LayoutSwitchTarget::Next => state.cycle_next_layout(),\n                    LayoutSwitchTarget::Prev => state.cycle_prev_layout(),\n                    LayoutSwitchTarget::Index(layout) => {\n                        let num_layouts = state.xkb().lock().unwrap().layouts().count();\n                        if usize::from(layout) >= num_layouts {\n                            warn!(\"requested layout doesn't exist\")\n                        } else {\n                            state.set_layout(Layout(layout.into()))\n                        }\n                    }\n                });\n            }\n            Action::MoveColumnLeft => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_left();\n                } else {\n                    self.niri.layout.move_left();\n                    self.maybe_warp_cursor_to_focus();\n                }\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveColumnRight => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_right();\n                } else {\n                    self.niri.layout.move_right();\n                    self.maybe_warp_cursor_to_focus();\n                }\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveColumnToFirst => {\n                self.niri.layout.move_column_to_first();\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveColumnToLast => {\n                self.niri.layout.move_column_to_last();\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveColumnLeftOrToMonitorLeft => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_left();\n                } else if let Some(output) = self.niri.output_left() {\n                    if self.niri.layout.move_column_left_or_to_output(&output)\n                        && !self.maybe_warp_cursor_to_focus_centered()\n                    {\n                        self.move_cursor_to_output(&output);\n                    } else {\n                        self.maybe_warp_cursor_to_focus();\n                    }\n                } else {\n                    self.niri.layout.move_left();\n                    self.maybe_warp_cursor_to_focus();\n                }\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveColumnRightOrToMonitorRight => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_right();\n                } else if let Some(output) = self.niri.output_right() {\n                    if self.niri.layout.move_column_right_or_to_output(&output)\n                        && !self.maybe_warp_cursor_to_focus_centered()\n                    {\n                        self.move_cursor_to_output(&output);\n                    } else {\n                        self.maybe_warp_cursor_to_focus();\n                    }\n                } else {\n                    self.niri.layout.move_right();\n                    self.maybe_warp_cursor_to_focus();\n                }\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowDown => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_down();\n                } else {\n                    self.niri.layout.move_down();\n                    self.maybe_warp_cursor_to_focus();\n                }\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowUp => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_up();\n                } else {\n                    self.niri.layout.move_up();\n                    self.maybe_warp_cursor_to_focus();\n                }\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowDownOrToWorkspaceDown => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_down();\n                } else {\n                    self.niri.layout.move_down_or_to_workspace_down();\n                    self.maybe_warp_cursor_to_focus();\n                }\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowUpOrToWorkspaceUp => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.move_up();\n                } else {\n                    self.niri.layout.move_up_or_to_workspace_up();\n                    self.maybe_warp_cursor_to_focus();\n                }\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::ConsumeOrExpelWindowLeft => {\n                self.niri.layout.consume_or_expel_window_left(None);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::ConsumeOrExpelWindowLeftById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.consume_or_expel_window_left(Some(&window));\n                    self.maybe_warp_cursor_to_focus();\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::ConsumeOrExpelWindowRight => {\n                self.niri.layout.consume_or_expel_window_right(None);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::ConsumeOrExpelWindowRightById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri\n                        .layout\n                        .consume_or_expel_window_right(Some(&window));\n                    self.maybe_warp_cursor_to_focus();\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::FocusColumnLeft => {\n                self.niri.layout.focus_left();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumnLeftUnderMouse => {\n                if let Some((output, ws)) = self.niri.workspace_under_cursor(true) {\n                    let ws_id = ws.id();\n                    let ws = {\n                        let mut workspaces = self.niri.layout.workspaces_mut();\n                        workspaces.find(|ws| ws.id() == ws_id).unwrap()\n                    };\n                    ws.focus_left();\n                    self.maybe_warp_cursor_to_focus();\n                    self.niri.layer_shell_on_demand_focus = None;\n                    self.niri.queue_redraw(&output);\n                }\n            }\n            Action::FocusColumnRight => {\n                self.niri.layout.focus_right();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumnRightUnderMouse => {\n                if let Some((output, ws)) = self.niri.workspace_under_cursor(true) {\n                    let ws_id = ws.id();\n                    let ws = {\n                        let mut workspaces = self.niri.layout.workspaces_mut();\n                        workspaces.find(|ws| ws.id() == ws_id).unwrap()\n                    };\n                    ws.focus_right();\n                    self.maybe_warp_cursor_to_focus();\n                    self.niri.layer_shell_on_demand_focus = None;\n                    self.niri.queue_redraw(&output);\n                }\n            }\n            Action::FocusColumnFirst => {\n                self.niri.layout.focus_column_first();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumnLast => {\n                self.niri.layout.focus_column_last();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumnRightOrFirst => {\n                self.niri.layout.focus_column_right_or_first();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumnLeftOrLast => {\n                self.niri.layout.focus_column_left_or_last();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumn(index) => {\n                self.niri.layout.focus_column(index);\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowOrMonitorUp => {\n                if let Some(output) = self.niri.output_up() {\n                    if self.niri.layout.focus_window_up_or_output(&output)\n                        && !self.maybe_warp_cursor_to_focus_centered()\n                    {\n                        self.move_cursor_to_output(&output);\n                    } else {\n                        self.maybe_warp_cursor_to_focus();\n                    }\n                } else {\n                    self.niri.layout.focus_up();\n                    self.maybe_warp_cursor_to_focus();\n                }\n                self.niri.layer_shell_on_demand_focus = None;\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowOrMonitorDown => {\n                if let Some(output) = self.niri.output_down() {\n                    if self.niri.layout.focus_window_down_or_output(&output)\n                        && !self.maybe_warp_cursor_to_focus_centered()\n                    {\n                        self.move_cursor_to_output(&output);\n                    } else {\n                        self.maybe_warp_cursor_to_focus();\n                    }\n                } else {\n                    self.niri.layout.focus_down();\n                    self.maybe_warp_cursor_to_focus();\n                }\n                self.niri.layer_shell_on_demand_focus = None;\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumnOrMonitorLeft => {\n                if let Some(output) = self.niri.output_left() {\n                    if self.niri.layout.focus_column_left_or_output(&output)\n                        && !self.maybe_warp_cursor_to_focus_centered()\n                    {\n                        self.move_cursor_to_output(&output);\n                    } else {\n                        self.maybe_warp_cursor_to_focus();\n                    }\n                } else {\n                    self.niri.layout.focus_left();\n                    self.maybe_warp_cursor_to_focus();\n                }\n                self.niri.layer_shell_on_demand_focus = None;\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusColumnOrMonitorRight => {\n                if let Some(output) = self.niri.output_right() {\n                    if self.niri.layout.focus_column_right_or_output(&output)\n                        && !self.maybe_warp_cursor_to_focus_centered()\n                    {\n                        self.move_cursor_to_output(&output);\n                    } else {\n                        self.maybe_warp_cursor_to_focus();\n                    }\n                } else {\n                    self.niri.layout.focus_right();\n                    self.maybe_warp_cursor_to_focus();\n                }\n                self.niri.layer_shell_on_demand_focus = None;\n\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowDown => {\n                self.niri.layout.focus_down();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowUp => {\n                self.niri.layout.focus_up();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowDownOrColumnLeft => {\n                self.niri.layout.focus_down_or_left();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowDownOrColumnRight => {\n                self.niri.layout.focus_down_or_right();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowUpOrColumnLeft => {\n                self.niri.layout.focus_up_or_left();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowUpOrColumnRight => {\n                self.niri.layout.focus_up_or_right();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowOrWorkspaceDown => {\n                self.niri.layout.focus_window_or_workspace_down();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowOrWorkspaceUp => {\n                self.niri.layout.focus_window_or_workspace_up();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowTop => {\n                self.niri.layout.focus_window_top();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowBottom => {\n                self.niri.layout.focus_window_bottom();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowDownOrTop => {\n                self.niri.layout.focus_window_down_or_top();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWindowUpOrBottom => {\n                self.niri.layout.focus_window_up_or_bottom();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowToWorkspaceDown(focus) => {\n                self.niri.layout.move_to_workspace_down(focus);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowToWorkspaceUp(focus) => {\n                self.niri.layout.move_to_workspace_up(focus);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowToWorkspace(reference, focus) => {\n                if let Some((mut output, index)) =\n                    self.niri.find_output_and_workspace_index(reference)\n                {\n                    // The source output is always the active output, so if the target output is\n                    // also the active output, we don't need to use move_to_output().\n                    if let Some(active) = self.niri.layout.active_output() {\n                        if output.as_ref() == Some(active) {\n                            output = None;\n                        }\n                    }\n\n                    let activate = if focus {\n                        ActivateWindow::Smart\n                    } else {\n                        ActivateWindow::No\n                    };\n\n                    if let Some(output) = output {\n                        self.niri\n                            .layout\n                            .move_to_output(None, &output, Some(index), activate);\n\n                        if focus {\n                            if !self.maybe_warp_cursor_to_focus_centered() {\n                                self.move_cursor_to_output(&output);\n                            }\n                        } else {\n                            self.maybe_warp_cursor_to_focus();\n                        }\n                    } else {\n                        self.niri.layout.move_to_workspace(None, index, activate);\n                        self.maybe_warp_cursor_to_focus();\n                    }\n\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::MoveWindowToWorkspaceById {\n                window_id: id,\n                reference,\n                focus,\n            } => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    if let Some((output, index)) =\n                        self.niri.find_output_and_workspace_index(reference)\n                    {\n                        let target_was_active = self\n                            .niri\n                            .layout\n                            .active_output()\n                            .is_some_and(|active| output.as_ref() == Some(active));\n\n                        let activate = if focus {\n                            ActivateWindow::Smart\n                        } else {\n                            ActivateWindow::No\n                        };\n\n                        if let Some(output) = output {\n                            self.niri.layout.move_to_output(\n                                Some(&window),\n                                &output,\n                                Some(index),\n                                activate,\n                            );\n\n                            // If the active output changed (window was moved and focused).\n                            #[allow(clippy::collapsible_if)]\n                            if !target_was_active\n                                && self.niri.layout.active_output() == Some(&output)\n                            {\n                                if !self.maybe_warp_cursor_to_focus_centered() {\n                                    self.move_cursor_to_output(&output);\n                                }\n                            }\n                        } else {\n                            self.niri\n                                .layout\n                                .move_to_workspace(Some(&window), index, activate);\n\n                            // If we focused the target window.\n                            let new_focus = self.niri.layout.focus();\n                            if new_focus.is_some_and(|win| win.window == window) {\n                                self.maybe_warp_cursor_to_focus();\n                            }\n                        }\n\n                        // FIXME: granular\n                        self.niri.queue_redraw_all();\n                    }\n                }\n            }\n            Action::MoveColumnToWorkspaceDown(focus) => {\n                self.niri.layout.move_column_to_workspace_down(focus);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveColumnToWorkspaceUp(focus) => {\n                self.niri.layout.move_column_to_workspace_up(focus);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveColumnToWorkspace(reference, focus) => {\n                if let Some((mut output, index)) =\n                    self.niri.find_output_and_workspace_index(reference)\n                {\n                    if let Some(active) = self.niri.layout.active_output() {\n                        if output.as_ref() == Some(active) {\n                            output = None;\n                        }\n                    }\n\n                    if let Some(output) = output {\n                        self.niri\n                            .layout\n                            .move_column_to_output(&output, Some(index), focus);\n                        if focus && !self.maybe_warp_cursor_to_focus_centered() {\n                            self.move_cursor_to_output(&output);\n                        }\n                    } else {\n                        self.niri.layout.move_column_to_workspace(index, focus);\n                        if focus {\n                            self.maybe_warp_cursor_to_focus();\n                        }\n                    }\n\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::MoveColumnToIndex(idx) => {\n                self.niri.layout.move_column_to_index(idx);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWorkspaceDown => {\n                self.niri.layout.switch_workspace_down();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWorkspaceDownUnderMouse => {\n                if let Some(output) = self.niri.output_under_cursor() {\n                    if let Some(mon) = self.niri.layout.monitor_for_output_mut(&output) {\n                        mon.switch_workspace_down();\n                        self.maybe_warp_cursor_to_focus();\n                        self.niri.layer_shell_on_demand_focus = None;\n                        self.niri.queue_redraw(&output);\n                    }\n                }\n            }\n            Action::FocusWorkspaceUp => {\n                self.niri.layout.switch_workspace_up();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusWorkspaceUpUnderMouse => {\n                if let Some(output) = self.niri.output_under_cursor() {\n                    if let Some(mon) = self.niri.layout.monitor_for_output_mut(&output) {\n                        mon.switch_workspace_up();\n                        self.maybe_warp_cursor_to_focus();\n                        self.niri.layer_shell_on_demand_focus = None;\n                        self.niri.queue_redraw(&output);\n                    }\n                }\n            }\n            Action::FocusWorkspace(reference) => {\n                if let Some((mut output, index)) =\n                    self.niri.find_output_and_workspace_index(reference)\n                {\n                    if let Some(active) = self.niri.layout.active_output() {\n                        if output.as_ref() == Some(active) {\n                            output = None;\n                        }\n                    }\n\n                    if let Some(output) = output {\n                        self.niri.layout.focus_output(&output);\n                        self.niri.layout.switch_workspace(index);\n                        if !self.maybe_warp_cursor_to_focus_centered() {\n                            self.move_cursor_to_output(&output);\n                        }\n                    } else {\n                        let config = &self.niri.config;\n                        if config.borrow().input.workspace_auto_back_and_forth {\n                            self.niri.layout.switch_workspace_auto_back_and_forth(index);\n                        } else {\n                            self.niri.layout.switch_workspace(index);\n                        }\n                        self.maybe_warp_cursor_to_focus();\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::FocusWorkspacePrevious => {\n                self.niri.layout.switch_workspace_previous();\n                self.maybe_warp_cursor_to_focus();\n                self.niri.layer_shell_on_demand_focus = None;\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWorkspaceDown => {\n                self.niri.layout.move_workspace_down();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWorkspaceUp => {\n                self.niri.layout.move_workspace_up();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWorkspaceToIndex(new_idx) => {\n                let new_idx = new_idx.saturating_sub(1);\n                self.niri.layout.move_workspace_to_idx(None, new_idx);\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWorkspaceToIndexByRef { new_idx, reference } => {\n                if let Some(res) = self.niri.find_output_and_workspace_index(reference) {\n                    let new_idx = new_idx.saturating_sub(1);\n                    self.niri.layout.move_workspace_to_idx(Some(res), new_idx);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::SetWorkspaceName(name) => {\n                self.niri.layout.set_workspace_name(name, None);\n            }\n            Action::SetWorkspaceNameByRef { name, reference } => {\n                self.niri.layout.set_workspace_name(name, Some(reference));\n            }\n            Action::UnsetWorkspaceName => {\n                self.niri.layout.unset_workspace_name(None);\n            }\n            Action::UnsetWorkSpaceNameByRef(reference) => {\n                self.niri.layout.unset_workspace_name(Some(reference));\n            }\n            Action::ConsumeWindowIntoColumn => {\n                self.niri.layout.consume_into_column();\n                // This does not cause immediate focus or window size change, so warping mouse to\n                // focus won't do anything here.\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::ExpelWindowFromColumn => {\n                self.niri.layout.expel_from_column();\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::SwapWindowRight => {\n                self.niri\n                    .layout\n                    .swap_window_in_direction(ScrollDirection::Right);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::SwapWindowLeft => {\n                self.niri\n                    .layout\n                    .swap_window_in_direction(ScrollDirection::Left);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::ToggleColumnTabbedDisplay => {\n                self.niri.layout.toggle_column_tabbed_display();\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::SetColumnDisplay(display) => {\n                self.niri.layout.set_column_display(display);\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::SwitchPresetColumnWidth => {\n                self.niri.layout.toggle_width(true);\n            }\n            Action::SwitchPresetColumnWidthBack => {\n                self.niri.layout.toggle_width(false);\n            }\n            Action::SwitchPresetWindowWidth => {\n                self.niri.layout.toggle_window_width(None, true);\n            }\n            Action::SwitchPresetWindowWidthBack => {\n                self.niri.layout.toggle_window_width(None, false);\n            }\n            Action::SwitchPresetWindowWidthById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_window_width(Some(&window), true);\n                }\n            }\n            Action::SwitchPresetWindowWidthBackById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_window_width(Some(&window), false);\n                }\n            }\n            Action::SwitchPresetWindowHeight => {\n                self.niri.layout.toggle_window_height(None, true);\n            }\n            Action::SwitchPresetWindowHeightBack => {\n                self.niri.layout.toggle_window_height(None, false);\n            }\n            Action::SwitchPresetWindowHeightById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_window_height(Some(&window), true);\n                }\n            }\n            Action::SwitchPresetWindowHeightBackById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_window_height(Some(&window), false);\n                }\n            }\n            Action::CenterColumn => {\n                self.niri.layout.center_column();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::CenterWindow => {\n                self.niri.layout.center_window(None);\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::CenterWindowById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.center_window(Some(&window));\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::CenterVisibleColumns => {\n                self.niri.layout.center_visible_columns();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MaximizeColumn => {\n                self.niri.layout.toggle_full_width();\n            }\n            Action::MaximizeWindowToEdges => {\n                let focus = self.niri.layout.focus().map(|m| m.window.clone());\n                if let Some(window) = focus {\n                    self.niri.layout.toggle_maximized(&window);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::MaximizeWindowToEdgesById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_maximized(&window);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::FocusMonitorLeft => {\n                if let Some(output) = self.niri.output_left() {\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n                }\n            }\n            Action::FocusMonitorRight => {\n                if let Some(output) = self.niri.output_right() {\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n                }\n            }\n            Action::FocusMonitorDown => {\n                if let Some(output) = self.niri.output_down() {\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n                }\n            }\n            Action::FocusMonitorUp => {\n                if let Some(output) = self.niri.output_up() {\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n                }\n            }\n            Action::FocusMonitorPrevious => {\n                if let Some(output) = self.niri.output_previous() {\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n                }\n            }\n            Action::FocusMonitorNext => {\n                if let Some(output) = self.niri.output_next() {\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n                }\n            }\n            Action::FocusMonitor(output) => {\n                if let Some(output) = self.niri.output_by_name_match(&output).cloned() {\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                    self.niri.layer_shell_on_demand_focus = None;\n                }\n            }\n            Action::MoveWindowToMonitorLeft => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_left_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_left() {\n                    self.niri\n                        .layout\n                        .move_to_output(None, &output, None, ActivateWindow::Smart);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWindowToMonitorRight => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_right_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_right() {\n                    self.niri\n                        .layout\n                        .move_to_output(None, &output, None, ActivateWindow::Smart);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWindowToMonitorDown => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_down_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_down() {\n                    self.niri\n                        .layout\n                        .move_to_output(None, &output, None, ActivateWindow::Smart);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWindowToMonitorUp => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_up_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_up() {\n                    self.niri\n                        .layout\n                        .move_to_output(None, &output, None, ActivateWindow::Smart);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWindowToMonitorPrevious => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_previous_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_previous() {\n                    self.niri\n                        .layout\n                        .move_to_output(None, &output, None, ActivateWindow::Smart);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWindowToMonitorNext => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_next_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_next() {\n                    self.niri\n                        .layout\n                        .move_to_output(None, &output, None, ActivateWindow::Smart);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWindowToMonitor(output) => {\n                if let Some(output) = self.niri.output_by_name_match(&output).cloned() {\n                    if self.niri.screenshot_ui.is_open() {\n                        self.move_cursor_to_output(&output);\n                        self.niri.screenshot_ui.move_to_output(output);\n                    } else {\n                        self.niri\n                            .layout\n                            .move_to_output(None, &output, None, ActivateWindow::Smart);\n                        self.niri.layout.focus_output(&output);\n                        if !self.maybe_warp_cursor_to_focus_centered() {\n                            self.move_cursor_to_output(&output);\n                        }\n                    }\n                }\n            }\n            Action::MoveWindowToMonitorById { id, output } => {\n                if let Some(output) = self.niri.output_by_name_match(&output).cloned() {\n                    let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                    let window = window.map(|(_, m)| m.window.clone());\n\n                    if let Some(window) = window {\n                        let target_was_active = self\n                            .niri\n                            .layout\n                            .active_output()\n                            .is_some_and(|active| output == *active);\n\n                        self.niri.layout.move_to_output(\n                            Some(&window),\n                            &output,\n                            None,\n                            ActivateWindow::Smart,\n                        );\n\n                        // If the active output changed (window was moved and focused).\n                        #[allow(clippy::collapsible_if)]\n                        if !target_was_active && self.niri.layout.active_output() == Some(&output) {\n                            if !self.maybe_warp_cursor_to_focus_centered() {\n                                self.move_cursor_to_output(&output);\n                            }\n                        }\n                    }\n                }\n            }\n            Action::MoveColumnToMonitorLeft => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_left_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_left() {\n                    self.niri.layout.move_column_to_output(&output, None, true);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveColumnToMonitorRight => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_right_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_right() {\n                    self.niri.layout.move_column_to_output(&output, None, true);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveColumnToMonitorDown => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_down_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_down() {\n                    self.niri.layout.move_column_to_output(&output, None, true);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveColumnToMonitorUp => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_up_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_up() {\n                    self.niri.layout.move_column_to_output(&output, None, true);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveColumnToMonitorPrevious => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_previous_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_previous() {\n                    self.niri.layout.move_column_to_output(&output, None, true);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveColumnToMonitorNext => {\n                if let Some(current_output) = self.niri.screenshot_ui.selection_output() {\n                    if let Some(target_output) = self.niri.output_next_of(current_output) {\n                        self.move_cursor_to_output(&target_output);\n                        self.niri.screenshot_ui.move_to_output(target_output);\n                    }\n                } else if let Some(output) = self.niri.output_next() {\n                    self.niri.layout.move_column_to_output(&output, None, true);\n                    self.niri.layout.focus_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveColumnToMonitor(output) => {\n                if let Some(output) = self.niri.output_by_name_match(&output).cloned() {\n                    if self.niri.screenshot_ui.is_open() {\n                        self.move_cursor_to_output(&output);\n                        self.niri.screenshot_ui.move_to_output(output);\n                    } else {\n                        self.niri.layout.move_column_to_output(&output, None, true);\n                        self.niri.layout.focus_output(&output);\n                        if !self.maybe_warp_cursor_to_focus_centered() {\n                            self.move_cursor_to_output(&output);\n                        }\n                    }\n                }\n            }\n            Action::SetColumnWidth(change) => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.set_width(change);\n\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                } else {\n                    self.niri.layout.set_column_width(change);\n                }\n            }\n            Action::SetWindowWidth(change) => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.set_width(change);\n\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                } else {\n                    self.niri.layout.set_window_width(None, change);\n                }\n            }\n            Action::SetWindowWidthById { id, change } => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.set_window_width(Some(&window), change);\n                }\n            }\n            Action::SetWindowHeight(change) => {\n                if self.niri.screenshot_ui.is_open() {\n                    self.niri.screenshot_ui.set_height(change);\n\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                } else {\n                    self.niri.layout.set_window_height(None, change);\n                }\n            }\n            Action::SetWindowHeightById { id, change } => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.set_window_height(Some(&window), change);\n                }\n            }\n            Action::ResetWindowHeight => {\n                self.niri.layout.reset_window_height(None);\n            }\n            Action::ResetWindowHeightById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.reset_window_height(Some(&window));\n                }\n            }\n            Action::ExpandColumnToAvailableWidth => {\n                self.niri.layout.expand_column_to_available_width();\n            }\n            Action::ShowHotkeyOverlay => {\n                if self.niri.hotkey_overlay.show() {\n                    self.niri.queue_redraw_all();\n\n                    #[cfg(feature = \"dbus\")]\n                    self.niri.a11y_announce_hotkey_overlay();\n                }\n            }\n            Action::MoveWorkspaceToMonitorLeft => {\n                if let Some(output) = self.niri.output_left() {\n                    self.niri.layout.move_workspace_to_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWorkspaceToMonitorRight => {\n                if let Some(output) = self.niri.output_right() {\n                    self.niri.layout.move_workspace_to_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWorkspaceToMonitorDown => {\n                if let Some(output) = self.niri.output_down() {\n                    self.niri.layout.move_workspace_to_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWorkspaceToMonitorUp => {\n                if let Some(output) = self.niri.output_up() {\n                    self.niri.layout.move_workspace_to_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWorkspaceToMonitorPrevious => {\n                if let Some(output) = self.niri.output_previous() {\n                    self.niri.layout.move_workspace_to_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWorkspaceToMonitorNext => {\n                if let Some(output) = self.niri.output_next() {\n                    self.niri.layout.move_workspace_to_output(&output);\n                    if !self.maybe_warp_cursor_to_focus_centered() {\n                        self.move_cursor_to_output(&output);\n                    }\n                }\n            }\n            Action::MoveWorkspaceToMonitor(new_output) => {\n                if let Some(new_output) = self.niri.output_by_name_match(&new_output).cloned() {\n                    if self.niri.layout.move_workspace_to_output(&new_output)\n                        && !self.maybe_warp_cursor_to_focus_centered()\n                    {\n                        self.move_cursor_to_output(&new_output);\n                    }\n                }\n            }\n            Action::MoveWorkspaceToMonitorByRef {\n                output_name,\n                reference,\n            } => {\n                if let Some((output, old_idx)) =\n                    self.niri.find_output_and_workspace_index(reference)\n                {\n                    if let Some(new_output) = self.niri.output_by_name_match(&output_name).cloned()\n                    {\n                        if self.niri.layout.move_workspace_to_output_by_id(\n                            old_idx,\n                            output,\n                            &new_output,\n                        ) {\n                            // Cursor warp already calls `queue_redraw_all`\n                            if !self.maybe_warp_cursor_to_focus_centered() {\n                                self.move_cursor_to_output(&new_output);\n                            }\n                        }\n                    }\n                }\n            }\n            Action::ToggleWindowFloating => {\n                self.niri.layout.toggle_window_floating(None);\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::ToggleWindowFloatingById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.toggle_window_floating(Some(&window));\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::MoveWindowToFloating => {\n                self.niri.layout.set_window_floating(None, true);\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowToFloatingById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.set_window_floating(Some(&window), true);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::MoveWindowToTiling => {\n                self.niri.layout.set_window_floating(None, false);\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveWindowToTilingById(id) => {\n                let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                let window = window.map(|(_, m)| m.window.clone());\n                if let Some(window) = window {\n                    self.niri.layout.set_window_floating(Some(&window), false);\n                    // FIXME: granular\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::FocusFloating => {\n                self.niri.layout.focus_floating();\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::FocusTiling => {\n                self.niri.layout.focus_tiling();\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::SwitchFocusBetweenFloatingAndTiling => {\n                self.niri.layout.switch_focus_floating_tiling();\n                self.maybe_warp_cursor_to_focus();\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::MoveFloatingWindowById { id, x, y } => {\n                let window = if let Some(id) = id {\n                    let window = self.niri.layout.windows().find(|(_, m)| m.id().get() == id);\n                    let window = window.map(|(_, m)| m.window.clone());\n                    if window.is_none() {\n                        return;\n                    }\n                    window\n                } else {\n                    None\n                };\n\n                self.niri\n                    .layout\n                    .move_floating_window(window.as_ref(), x, y, true);\n                // FIXME: granular\n                self.niri.queue_redraw_all();\n            }\n            Action::ToggleWindowRuleOpacity => {\n                let active_window = self\n                    .niri\n                    .layout\n                    .active_workspace_mut()\n                    .and_then(|ws| ws.active_window_mut());\n                if let Some(window) = active_window {\n                    if window.rules().opacity.is_some_and(|o| o != 1.) {\n                        window.toggle_ignore_opacity_window_rule();\n                        // FIXME: granular\n                        self.niri.queue_redraw_all();\n                    }\n                }\n            }\n            Action::ToggleWindowRuleOpacityById(id) => {\n                let window = self\n                    .niri\n                    .layout\n                    .workspaces_mut()\n                    .find_map(|ws| ws.windows_mut().find(|w| w.id().get() == id));\n                if let Some(window) = window {\n                    if window.rules().opacity.is_some_and(|o| o != 1.) {\n                        window.toggle_ignore_opacity_window_rule();\n                        // FIXME: granular\n                        self.niri.queue_redraw_all();\n                    }\n                }\n            }\n            Action::SetDynamicCastWindow => {\n                let id = self\n                    .niri\n                    .layout\n                    .active_workspace()\n                    .and_then(|ws| ws.active_window())\n                    .map(|mapped| mapped.id().get());\n                if let Some(id) = id {\n                    self.set_dynamic_cast_target(CastTarget::Window { id });\n                }\n            }\n            Action::SetDynamicCastWindowById(id) => {\n                let layout = &self.niri.layout;\n                if layout.windows().any(|(_, mapped)| mapped.id().get() == id) {\n                    self.set_dynamic_cast_target(CastTarget::Window { id });\n                }\n            }\n            Action::SetDynamicCastMonitor(output) => {\n                let output = match output {\n                    None => self.niri.layout.active_output(),\n                    Some(name) => self.niri.output_by_name_match(&name),\n                };\n                if let Some(output) = output {\n                    self.set_dynamic_cast_target(CastTarget::output(output));\n                }\n            }\n            Action::ClearDynamicCastTarget => {\n                self.set_dynamic_cast_target(CastTarget::Nothing);\n            }\n            Action::StopCast(session_id) => {\n                self.niri.stop_cast(CastSessionId::from(session_id));\n            }\n            Action::ToggleOverview => {\n                self.niri.layout.toggle_overview();\n                self.niri.queue_redraw_all();\n            }\n            Action::OpenOverview => {\n                if self.niri.layout.open_overview() {\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::CloseOverview => {\n                if self.niri.layout.close_overview() {\n                    self.niri.queue_redraw_all();\n                }\n            }\n            Action::ToggleWindowUrgent(id) => {\n                let window = self\n                    .niri\n                    .layout\n                    .workspaces_mut()\n                    .find_map(|ws| ws.windows_mut().find(|w| w.id().get() == id));\n                if let Some(window) = window {\n                    let urgent = window.is_urgent();\n                    window.set_urgent(!urgent);\n                }\n                self.niri.queue_redraw_all();\n            }\n            Action::SetWindowUrgent(id) => {\n                let window = self\n                    .niri\n                    .layout\n                    .workspaces_mut()\n                    .find_map(|ws| ws.windows_mut().find(|w| w.id().get() == id));\n                if let Some(window) = window {\n                    window.set_urgent(true);\n                }\n                self.niri.queue_redraw_all();\n            }\n            Action::UnsetWindowUrgent(id) => {\n                let window = self\n                    .niri\n                    .layout\n                    .workspaces_mut()\n                    .find_map(|ws| ws.windows_mut().find(|w| w.id().get() == id));\n                if let Some(window) = window {\n                    window.set_urgent(false);\n                }\n                self.niri.queue_redraw_all();\n            }\n            Action::LoadConfigFile(path) => {\n                if let Some(watcher) = &self.niri.config_file_watcher {\n                    watcher.load_config(path);\n                }\n            }\n            Action::MruConfirm => {\n                self.confirm_mru();\n            }\n            Action::MruCancel => {\n                self.niri.cancel_mru();\n            }\n            Action::MruAdvance {\n                direction,\n                scope,\n                filter,\n            } => {\n                if self.niri.window_mru_ui.is_open() {\n                    self.niri.window_mru_ui.advance(direction, filter);\n                    self.niri.queue_redraw_mru_output();\n                } else if self.niri.config.borrow().recent_windows.on {\n                    self.niri.mru_apply_keyboard_commit();\n\n                    let config = self.niri.config.borrow();\n                    let scope = scope.unwrap_or(self.niri.window_mru_ui.scope());\n\n                    let mut wmru = WindowMru::new(&self.niri);\n                    if !wmru.is_empty() {\n                        wmru.set_scope(scope);\n                        if let Some(filter) = filter {\n                            wmru.set_filter(filter);\n                        }\n\n                        if let Some(output) = self.niri.layout.active_output() {\n                            self.niri.window_mru_ui.open(\n                                self.niri.clock.clone(),\n                                wmru,\n                                output.clone(),\n                            );\n\n                            // Only select the *next* window if some window (which should be the\n                            // first one) is already focused. If nothing is focused, keep the first\n                            // window (which is logically the \"previously selected\" one).\n                            let keep_first = direction == MruDirection::Forward\n                                && self.niri.layout.focus().is_none();\n                            if !keep_first {\n                                self.niri.window_mru_ui.advance(direction, None);\n                            }\n\n                            drop(config);\n                            self.niri.queue_redraw_all();\n                        }\n                    }\n                }\n            }\n            Action::MruCloseCurrentWindow => {\n                if self.niri.window_mru_ui.is_open() {\n                    if let Some(id) = self.niri.window_mru_ui.current_window_id() {\n                        if let Some(w) = self.niri.find_window_by_id(id) {\n                            if let Some(tl) = w.toplevel() {\n                                tl.send_close();\n                            }\n                        }\n                    }\n                }\n            }\n            Action::MruFirst => {\n                if self.niri.window_mru_ui.is_open() {\n                    self.niri.window_mru_ui.first();\n                    self.niri.queue_redraw_mru_output();\n                }\n            }\n            Action::MruLast => {\n                if self.niri.window_mru_ui.is_open() {\n                    self.niri.window_mru_ui.last();\n                    self.niri.queue_redraw_mru_output();\n                }\n            }\n            Action::MruSetScope(scope) => {\n                if self.niri.window_mru_ui.is_open() {\n                    self.niri.window_mru_ui.set_scope(scope);\n                    self.niri.queue_redraw_mru_output();\n                }\n            }\n            Action::MruCycleScope => {\n                if self.niri.window_mru_ui.is_open() {\n                    self.niri.window_mru_ui.cycle_scope();\n                    self.niri.queue_redraw_mru_output();\n                }\n            }\n        }\n    }\n\n    fn on_pointer_motion<I: InputBackend>(&mut self, event: I::PointerMotionEvent) {\n        let was_inside_hot_corner = self.niri.pointer_inside_hot_corner;\n        // Any of the early returns here mean that the pointer is not inside the hot corner.\n        self.niri.pointer_inside_hot_corner = false;\n\n        // We need an output to be able to move the pointer.\n        if self.niri.global_space.outputs().next().is_none() {\n            return;\n        }\n\n        let serial = SERIAL_COUNTER.next_serial();\n\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        let pos = pointer.current_location();\n\n        // We have an output, so we can compute the new location and focus.\n        let mut new_pos = pos + event.delta();\n\n        // We received an event for the regular pointer, so show it now.\n        self.niri.pointer_visibility = PointerVisibility::Visible;\n        self.niri.tablet_cursor_location = None;\n\n        // Check if we have an active pointer constraint.\n        //\n        // FIXME: ideally this should use the pointer focus with up-to-date global location.\n        let mut pointer_confined = None;\n        if let Some(under) = &self.niri.pointer_contents.surface {\n            // No need to check if the pointer focus surface matches, because here we're checking\n            // for an already-active constraint, and the constraint is deactivated when the focused\n            // surface changes.\n            let pos_within_surface = pos - under.1;\n\n            let mut pointer_locked = false;\n            with_pointer_constraint(&under.0, &pointer, |constraint| {\n                let Some(constraint) = constraint else { return };\n                if !constraint.is_active() {\n                    return;\n                }\n\n                // Constraint does not apply if not within region.\n                if let Some(region) = constraint.region() {\n                    if !region.contains(pos_within_surface.to_i32_round()) {\n                        return;\n                    }\n                }\n\n                match &*constraint {\n                    PointerConstraint::Locked(_locked) => {\n                        pointer_locked = true;\n                    }\n                    PointerConstraint::Confined(confine) => {\n                        pointer_confined = Some((under.clone(), confine.region().cloned()));\n                    }\n                }\n            });\n\n            // If the pointer is locked, only send relative motion.\n            if pointer_locked {\n                pointer.relative_motion(\n                    self,\n                    Some(under.clone()),\n                    &RelativeMotionEvent {\n                        delta: event.delta(),\n                        delta_unaccel: event.delta_unaccel(),\n                        utime: event.time(),\n                    },\n                );\n\n                pointer.frame(self);\n\n                // I guess a redraw to hide the tablet cursor could be nice? Doesn't matter too\n                // much here I think.\n                return;\n            }\n        }\n\n        // Warp pointer across the screen during the spatial movement grabs.\n        let spatial_grab = pointer.with_grab(|_, grab| {\n            let grab = grab.as_any();\n            if let Some(grab) = grab.downcast_ref::<SpatialMovementGrab>() {\n                if let Some(output) = grab.view_offset_output() {\n                    return Some((output.clone(), true));\n                } else if let Some(output) = grab.workspace_switch_output() {\n                    return Some((output.clone(), false));\n                }\n            } else if let Some(grab) = grab.downcast_ref::<MoveGrab>() {\n                if let Some(output) = grab.view_offset_output() {\n                    return Some((output.clone(), true));\n                }\n            }\n            None\n        });\n        if let Some((output, horizontal)) = spatial_grab.flatten() {\n            if let Some(geo) = self.niri.global_space.output_geometry(&output) {\n                let geo = geo.to_f64();\n                if horizontal {\n                    new_pos.x = (new_pos.x - geo.loc.x).rem_euclid(geo.size.w) + geo.loc.x;\n                    new_pos.y = new_pos.y.clamp(geo.loc.y, geo.loc.y + geo.size.h - 1.);\n                } else {\n                    new_pos.x = new_pos.x.clamp(geo.loc.x, geo.loc.x + geo.size.w - 1.);\n                    new_pos.y = (new_pos.y - geo.loc.y).rem_euclid(geo.size.h) + geo.loc.y;\n                }\n            }\n        }\n\n        if self\n            .niri\n            .global_space\n            .output_under(new_pos)\n            .next()\n            .is_none()\n        {\n            // We ended up outside the outputs and need to clip the movement.\n            if let Some(output) = self.niri.global_space.output_under(pos).next() {\n                // The pointer was previously on some output. Clip the movement against its\n                // boundaries.\n                let geom = self.niri.global_space.output_geometry(output).unwrap();\n                new_pos.x = new_pos\n                    .x\n                    .clamp(geom.loc.x as f64, (geom.loc.x + geom.size.w - 1) as f64);\n                new_pos.y = new_pos\n                    .y\n                    .clamp(geom.loc.y as f64, (geom.loc.y + geom.size.h - 1) as f64);\n            } else {\n                // The pointer was not on any output in the first place. Find one for it.\n                // Let's do the simple thing and just put it on the first output.\n                let output = self.niri.global_space.outputs().next().unwrap();\n                let geom = self.niri.global_space.output_geometry(output).unwrap();\n                new_pos = center(geom).to_f64();\n            }\n        }\n\n        if let Some(output) = self.niri.screenshot_ui.selection_output() {\n            let geom = self.niri.global_space.output_geometry(output).unwrap();\n            let mut point = (new_pos - geom.loc.to_f64())\n                .to_physical(output.current_scale().fractional_scale())\n                .to_i32_round::<i32>();\n\n            let size = output.current_mode().unwrap().size;\n            let transform = output.current_transform();\n            let size = transform.transform_size(size);\n            point.x = point.x.clamp(0, size.w - 1);\n            point.y = point.y.clamp(0, size.h - 1);\n\n            self.niri.screenshot_ui.pointer_motion(point, None);\n        }\n\n        if let Some(mru_output) = self.niri.window_mru_ui.output() {\n            if let Some((output, pos_within_output)) = self.niri.output_under(new_pos) {\n                if mru_output == output {\n                    self.niri.window_mru_ui.pointer_motion(pos_within_output);\n                }\n            }\n        }\n\n        let under = self.niri.contents_under(new_pos);\n\n        // Handle confined pointer.\n        if let Some((focus_surface, region)) = pointer_confined {\n            let mut prevent = false;\n\n            // Prevent the pointer from leaving the focused surface.\n            if Some(&focus_surface.0) != under.surface.as_ref().map(|(s, _)| s) {\n                prevent = true;\n            }\n\n            // Prevent the pointer from leaving the confine region, if any.\n            if let Some(region) = region {\n                let new_pos_within_surface = new_pos - focus_surface.1;\n                if !region.contains(new_pos_within_surface.to_i32_round()) {\n                    prevent = true;\n                }\n            }\n\n            if prevent {\n                pointer.relative_motion(\n                    self,\n                    Some(focus_surface),\n                    &RelativeMotionEvent {\n                        delta: event.delta(),\n                        delta_unaccel: event.delta_unaccel(),\n                        utime: event.time(),\n                    },\n                );\n\n                pointer.frame(self);\n\n                return;\n            }\n        }\n\n        self.niri.handle_focus_follows_mouse(&under);\n\n        self.niri.pointer_contents.clone_from(&under);\n\n        pointer.motion(\n            self,\n            under.surface.clone(),\n            &MotionEvent {\n                location: new_pos,\n                serial,\n                time: event.time_msec(),\n            },\n        );\n\n        pointer.relative_motion(\n            self,\n            under.surface,\n            &RelativeMotionEvent {\n                delta: event.delta(),\n                delta_unaccel: event.delta_unaccel(),\n                utime: event.time(),\n            },\n        );\n\n        pointer.frame(self);\n\n        // contents_under() will return no surface when the hot corner should trigger, so\n        // pointer.motion() will set the current focus to None.\n        if under.hot_corner && pointer.current_focus().is_none() {\n            if !was_inside_hot_corner\n                && pointer\n                    .with_grab(|_, grab| grab_allows_hot_corner(grab))\n                    .unwrap_or(true)\n            {\n                self.niri.layout.toggle_overview();\n            }\n            self.niri.pointer_inside_hot_corner = true;\n        }\n\n        // Activate a new confinement if necessary.\n        self.niri.maybe_activate_pointer_constraint();\n\n        // Inform the layout of an ongoing DnD operation.\n        let is_dnd_grab = pointer\n            .with_grab(|_, grab| Self::is_dnd_grab(grab.as_any()))\n            .unwrap_or(false);\n        if is_dnd_grab {\n            if let Some((output, pos_within_output)) = self.niri.output_under(new_pos) {\n                let output = output.clone();\n                self.niri.layout.dnd_update(output, pos_within_output);\n            }\n        }\n\n        // Redraw to update the cursor position.\n        // FIXME: redraw only outputs overlapping the cursor.\n        self.niri.queue_redraw_all();\n    }\n\n    fn on_pointer_motion_absolute<I: InputBackend>(\n        &mut self,\n        event: I::PointerMotionAbsoluteEvent,\n    ) {\n        let was_inside_hot_corner = self.niri.pointer_inside_hot_corner;\n        // Any of the early returns here mean that the pointer is not inside the hot corner.\n        self.niri.pointer_inside_hot_corner = false;\n\n        let Some(pos) = self.compute_absolute_location(&event, None).or_else(|| {\n            self.global_bounding_rectangle().map(|output_geo| {\n                event.position_transformed(output_geo.size) + output_geo.loc.to_f64()\n            })\n        }) else {\n            return;\n        };\n\n        let serial = SERIAL_COUNTER.next_serial();\n\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if let Some(output) = self.niri.screenshot_ui.selection_output() {\n            let geom = self.niri.global_space.output_geometry(output).unwrap();\n            let mut point = (pos - geom.loc.to_f64())\n                .to_physical(output.current_scale().fractional_scale())\n                .to_i32_round::<i32>();\n\n            let size = output.current_mode().unwrap().size;\n            let transform = output.current_transform();\n            let size = transform.transform_size(size);\n            point.x = point.x.clamp(0, size.w - 1);\n            point.y = point.y.clamp(0, size.h - 1);\n\n            self.niri.screenshot_ui.pointer_motion(point, None);\n        }\n\n        if let Some(mru_output) = self.niri.window_mru_ui.output() {\n            if let Some((output, pos_within_output)) = self.niri.output_under(pos) {\n                if mru_output == output {\n                    self.niri.window_mru_ui.pointer_motion(pos_within_output);\n                }\n            }\n        }\n\n        let under = self.niri.contents_under(pos);\n\n        self.niri.handle_focus_follows_mouse(&under);\n\n        self.niri.pointer_contents.clone_from(&under);\n\n        pointer.motion(\n            self,\n            under.surface,\n            &MotionEvent {\n                location: pos,\n                serial,\n                time: event.time_msec(),\n            },\n        );\n\n        pointer.frame(self);\n\n        // contents_under() will return no surface when the hot corner should trigger, so\n        // pointer.motion() will set the current focus to None.\n        if under.hot_corner && pointer.current_focus().is_none() {\n            if !was_inside_hot_corner\n                && pointer\n                    .with_grab(|_, grab| grab_allows_hot_corner(grab))\n                    .unwrap_or(true)\n            {\n                self.niri.layout.toggle_overview();\n            }\n            self.niri.pointer_inside_hot_corner = true;\n        }\n\n        self.niri.maybe_activate_pointer_constraint();\n\n        // We moved the pointer, show it.\n        self.niri.pointer_visibility = PointerVisibility::Visible;\n\n        // We moved the regular pointer, so show it now.\n        self.niri.tablet_cursor_location = None;\n\n        // Inform the layout of an ongoing DnD operation.\n        let is_dnd_grab = pointer\n            .with_grab(|_, grab| Self::is_dnd_grab(grab.as_any()))\n            .unwrap_or(false);\n        if is_dnd_grab {\n            if let Some((output, pos_within_output)) = self.niri.output_under(pos) {\n                let output = output.clone();\n                self.niri.layout.dnd_update(output, pos_within_output);\n            }\n        }\n\n        // Redraw to update the cursor position.\n        // FIXME: redraw only outputs overlapping the cursor.\n        self.niri.queue_redraw_all();\n    }\n\n    fn on_pointer_button<I: InputBackend>(&mut self, event: I::PointerButtonEvent) {\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        let serial = SERIAL_COUNTER.next_serial();\n\n        let button = event.button();\n\n        let button_code = event.button_code();\n\n        let button_state = event.state();\n\n        let mod_key = self.backend.mod_key(&self.niri.config.borrow());\n\n        // Ignore release events for mouse clicks that triggered a bind.\n        if self.niri.suppressed_buttons.remove(&button_code) {\n            return;\n        }\n\n        if ButtonState::Pressed == button_state {\n            let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();\n            let modifiers = modifiers_from_state(mods);\n\n            let mut is_mru_open = false;\n            if let Some(mru_output) = self.niri.window_mru_ui.output() {\n                is_mru_open = true;\n                if let Some(MouseButton::Left) = button {\n                    let location = pointer.current_location();\n                    let (output, pos_within_output) = self.niri.output_under(location).unwrap();\n                    if mru_output == output {\n                        let id = self.niri.window_mru_ui.pointer_motion(pos_within_output);\n                        if id.is_some() {\n                            self.confirm_mru();\n                        } else {\n                            self.niri.cancel_mru();\n                        }\n                    } else {\n                        self.niri.cancel_mru();\n                    }\n\n                    self.niri.suppressed_buttons.insert(button_code);\n                    return;\n                }\n            }\n\n            if is_mru_open || self.niri.mods_with_mouse_binds.contains(&modifiers) {\n                if let Some(bind) = match button {\n                    Some(MouseButton::Left) => Some(Trigger::MouseLeft),\n                    Some(MouseButton::Right) => Some(Trigger::MouseRight),\n                    Some(MouseButton::Middle) => Some(Trigger::MouseMiddle),\n                    Some(MouseButton::Back) => Some(Trigger::MouseBack),\n                    Some(MouseButton::Forward) => Some(Trigger::MouseForward),\n                    _ => None,\n                }\n                .and_then(|trigger| {\n                    let config = self.niri.config.borrow();\n                    let bindings =\n                        make_binds_iter(&config, &mut self.niri.window_mru_ui, modifiers);\n                    find_configured_bind(bindings, mod_key, trigger, mods)\n                }) {\n                    self.niri.suppressed_buttons.insert(button_code);\n                    self.handle_bind(bind.clone());\n                    return;\n                };\n            }\n\n            // We received an event for the regular pointer, so show it now.\n            self.niri.pointer_visibility = PointerVisibility::Visible;\n            self.niri.tablet_cursor_location = None;\n\n            let is_overview_open = self.niri.layout.is_overview_open();\n\n            if is_overview_open && !pointer.is_grabbed() && button == Some(MouseButton::Right) {\n                if let Some((output, ws)) = self.niri.workspace_under_cursor(true) {\n                    let ws_id = ws.id();\n                    let ws_idx = self.niri.layout.find_workspace_by_id(ws_id).unwrap().0;\n\n                    self.niri.layout.focus_output(&output);\n\n                    let location = pointer.current_location();\n                    let start_data = PointerGrabStartData {\n                        focus: None,\n                        button: button_code,\n                        location,\n                    };\n                    self.niri\n                        .layout\n                        .view_offset_gesture_begin(&output, Some(ws_idx), false);\n                    let grab = SpatialMovementGrab::new(start_data, output, ws_id, true);\n                    pointer.set_grab(self, grab, serial, Focus::Clear);\n                    self.niri\n                        .cursor_manager\n                        .set_cursor_image(CursorImageStatus::Named(CursorIcon::AllScroll));\n\n                    // FIXME: granular.\n                    self.niri.queue_redraw_all();\n                    return;\n                }\n            }\n\n            if button == Some(MouseButton::Middle) && !pointer.is_grabbed() {\n                let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());\n                if mod_down {\n                    let output_ws = if is_overview_open {\n                        self.niri.workspace_under_cursor(true)\n                    } else {\n                        // We don't want to accidentally \"catch\" the wrong workspace during\n                        // animations.\n                        self.niri.output_under_cursor().and_then(|output| {\n                            let mon = self.niri.layout.monitor_for_output(&output)?;\n                            Some((output, mon.active_workspace_ref()))\n                        })\n                    };\n\n                    if let Some((output, ws)) = output_ws {\n                        let ws_id = ws.id();\n\n                        self.niri.layout.focus_output(&output);\n\n                        let location = pointer.current_location();\n                        let start_data = PointerGrabStartData {\n                            focus: None,\n                            button: button_code,\n                            location,\n                        };\n                        let grab = SpatialMovementGrab::new(start_data, output, ws_id, false);\n                        pointer.set_grab(self, grab, serial, Focus::Clear);\n                        self.niri\n                            .cursor_manager\n                            .set_cursor_image(CursorImageStatus::Named(CursorIcon::AllScroll));\n\n                        // FIXME: granular.\n                        self.niri.queue_redraw_all();\n\n                        // Don't activate the window under the cursor to avoid unnecessary\n                        // scrolling when e.g. Mod+MMB clicking on a partially off-screen window.\n                        return;\n                    }\n                }\n            }\n\n            if let Some(mapped) = self.niri.window_under_cursor() {\n                let window = mapped.window.clone();\n\n                // Check if we need to start an interactive move.\n                if button == Some(MouseButton::Left) && !pointer.is_grabbed() {\n                    let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());\n                    if is_overview_open || mod_down {\n                        let location = pointer.current_location();\n\n                        if !is_overview_open {\n                            self.niri.layout.activate_window(&window);\n                        }\n\n                        let start_data = PointerGrabStartData {\n                            focus: None,\n                            button: button_code,\n                            location,\n                        };\n                        let start_data = PointerOrTouchStartData::Pointer(start_data);\n                        let icon = CursorIcon::Grabbing;\n                        if let Some(grab) =\n                            MoveGrab::new(self, start_data, window.clone(), false, Some(icon))\n                        {\n                            pointer.set_grab(self, grab, serial, Focus::Clear);\n\n                            // Set the cursor to Grabbing right away for Mod+LMB since it doesn't\n                            // do any other gesture.\n                            //\n                            // In the overview, we click to activate window and close the overview,\n                            // in this case setting the cursor right away would be distracting.\n                            if !is_overview_open {\n                                self.niri\n                                    .cursor_manager\n                                    .set_cursor_image(CursorImageStatus::Named(icon));\n                            }\n                        }\n                    }\n                }\n                // Check if we need to start an interactive resize.\n                else if button == Some(MouseButton::Right) && !pointer.is_grabbed() {\n                    let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());\n                    if mod_down {\n                        let location = pointer.current_location();\n                        let (output, pos_within_output) = self.niri.output_under(location).unwrap();\n                        let edges = self\n                            .niri\n                            .layout\n                            .resize_edges_under(output, pos_within_output)\n                            .unwrap_or(ResizeEdge::empty());\n\n                        if !edges.is_empty() {\n                            // See if we got a double resize-click gesture.\n                            // FIXME: deduplicate with resize_request in xdg-shell somehow.\n                            let time = get_monotonic_time();\n                            let last_cell = mapped.last_interactive_resize_start();\n                            let mut last = last_cell.get();\n                            last_cell.set(Some((time, edges)));\n\n                            // Floating windows don't have either of the double-resize-click\n                            // gestures, so just allow it to resize.\n                            if mapped.is_floating() {\n                                last = None;\n                                last_cell.set(None);\n                            }\n\n                            if let Some((last_time, last_edges)) = last {\n                                if time.saturating_sub(last_time) <= DOUBLE_CLICK_TIME {\n                                    // Allow quick resize after a triple click.\n                                    last_cell.set(None);\n\n                                    let intersection = edges.intersection(last_edges);\n                                    if intersection.intersects(ResizeEdge::LEFT_RIGHT) {\n                                        // FIXME: don't activate once we can pass specific windows\n                                        // to actions.\n                                        self.niri.layout.activate_window(&window);\n                                        self.niri.layout.toggle_full_width();\n                                    }\n                                    if intersection.intersects(ResizeEdge::TOP_BOTTOM) {\n                                        self.niri.layout.activate_window(&window);\n                                        self.niri.layout.reset_window_height(Some(&window));\n                                    }\n                                    // FIXME: granular.\n                                    self.niri.queue_redraw_all();\n                                    return;\n                                }\n                            }\n\n                            self.niri.layout.activate_window(&window);\n\n                            if self\n                                .niri\n                                .layout\n                                .interactive_resize_begin(window.clone(), edges)\n                            {\n                                let start_data = PointerGrabStartData {\n                                    focus: None,\n                                    button: button_code,\n                                    location,\n                                };\n                                let grab = ResizeGrab::new(start_data, window.clone());\n                                pointer.set_grab(self, grab, serial, Focus::Clear);\n                                self.niri.cursor_manager.set_cursor_image(\n                                    CursorImageStatus::Named(edges.cursor_icon()),\n                                );\n                            }\n                        }\n                    }\n                }\n\n                if !is_overview_open {\n                    self.niri.layout.activate_window(&window);\n                }\n\n                // FIXME: granular.\n                self.niri.queue_redraw_all();\n            } else if let Some((output, ws)) = is_overview_open\n                .then(|| self.niri.workspace_under_cursor(false))\n                .flatten()\n            {\n                let ws_idx = self.niri.layout.find_workspace_by_id(ws.id()).unwrap().0;\n\n                self.niri.layout.focus_output(&output);\n                self.niri.layout.toggle_overview_to_workspace(ws_idx);\n\n                // FIXME: granular.\n                self.niri.queue_redraw_all();\n            } else if let Some(output) = self.niri.output_under_cursor() {\n                self.niri.layout.focus_output(&output);\n\n                // FIXME: granular.\n                self.niri.queue_redraw_all();\n            }\n        };\n\n        self.update_pointer_contents();\n\n        if ButtonState::Pressed == button_state {\n            let layer_under = self.niri.pointer_contents.layer.clone();\n            self.niri.focus_layer_surface_if_on_demand(layer_under);\n        }\n\n        if button == Some(MouseButton::Left) && self.niri.screenshot_ui.is_open() {\n            if button_state == ButtonState::Pressed {\n                let pos = pointer.current_location();\n                if let Some((output, _)) = self.niri.output_under(pos) {\n                    let output = output.clone();\n                    let geom = self.niri.global_space.output_geometry(&output).unwrap();\n                    let mut point = (pos - geom.loc.to_f64())\n                        .to_physical(output.current_scale().fractional_scale())\n                        .to_i32_round();\n\n                    let size = output.current_mode().unwrap().size;\n                    let transform = output.current_transform();\n                    let size = transform.transform_size(size);\n                    point.x = min(size.w - 1, point.x);\n                    point.y = min(size.h - 1, point.y);\n\n                    if self.niri.screenshot_ui.pointer_down(output, point, None) {\n                        self.niri.queue_redraw_all();\n                    }\n                }\n            } else if let Some(capture) = self.niri.screenshot_ui.pointer_up(None) {\n                if capture {\n                    self.confirm_screenshot(true);\n                } else {\n                    self.niri.queue_redraw_all();\n                }\n            }\n        }\n\n        pointer.button(\n            self,\n            &ButtonEvent {\n                button: button_code,\n                state: button_state,\n                serial,\n                time: event.time_msec(),\n            },\n        );\n        pointer.frame(self);\n    }\n\n    fn on_pointer_axis<I: InputBackend>(&mut self, event: I::PointerAxisEvent) {\n        let pointer = &self.niri.seat.get_pointer().unwrap();\n\n        let source = event.source();\n\n        let mod_key = self.backend.mod_key(&self.niri.config.borrow());\n\n        // We received an event for the regular pointer, so show it now. This is also needed for\n        // update_pointer_contents() below to return the real contents, necessary for the pointer\n        // axis event to reach the window.\n        self.niri.pointer_visibility = PointerVisibility::Visible;\n        self.niri.tablet_cursor_location = None;\n\n        let timestamp = Duration::from_micros(event.time());\n\n        let horizontal_amount_v120 = event.amount_v120(Axis::Horizontal);\n        let vertical_amount_v120 = event.amount_v120(Axis::Vertical);\n\n        let is_overview_open = self.niri.layout.is_overview_open();\n\n        // We should only handle scrolling in the overview if the pointer is not over a (top or\n        // overlay) layer surface.\n        let should_handle_in_overview = if is_overview_open {\n            // FIXME: ideally this should happen after updating the pointer contents, which happens\n            // below. However, our pointer actions are supposed to act on the old surface, before\n            // updating the pointer contents.\n            pointer\n                .current_focus()\n                .map(|surface| self.niri.find_root_shell_surface(&surface))\n                .is_none_or(|root| {\n                    !self\n                        .niri\n                        .mapped_layer_surfaces\n                        .keys()\n                        .any(|layer| *layer.wl_surface() == root)\n                })\n        } else {\n            false\n        };\n\n        let is_mru_open = self.niri.window_mru_ui.is_open();\n\n        // Handle wheel scroll bindings.\n        if source == AxisSource::Wheel {\n            // If we have a scroll bind with current modifiers, then accumulate and don't pass to\n            // Wayland. If there's no bind, reset the accumulator.\n            let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();\n            let modifiers = modifiers_from_state(mods);\n            let should_handle = should_handle_in_overview\n                || is_mru_open\n                || self.niri.mods_with_wheel_binds.contains(&modifiers);\n            if should_handle {\n                let horizontal = horizontal_amount_v120.unwrap_or(0.);\n                let ticks = self.niri.horizontal_wheel_tracker.accumulate(horizontal);\n                if ticks != 0 {\n                    let (bind_left, bind_right) =\n                        if should_handle_in_overview && modifiers.is_empty() {\n                            let bind_left = Some(Bind {\n                                key: Key {\n                                    trigger: Trigger::WheelScrollLeft,\n                                    modifiers: Modifiers::empty(),\n                                },\n                                action: Action::FocusColumnLeftUnderMouse,\n                                repeat: true,\n                                cooldown: None,\n                                allow_when_locked: false,\n                                allow_inhibiting: false,\n                                hotkey_overlay_title: None,\n                            });\n                            let bind_right = Some(Bind {\n                                key: Key {\n                                    trigger: Trigger::WheelScrollRight,\n                                    modifiers: Modifiers::empty(),\n                                },\n                                action: Action::FocusColumnRightUnderMouse,\n                                repeat: true,\n                                cooldown: None,\n                                allow_when_locked: false,\n                                allow_inhibiting: false,\n                                hotkey_overlay_title: None,\n                            });\n                            (bind_left, bind_right)\n                        } else {\n                            let config = self.niri.config.borrow();\n                            let bindings =\n                                make_binds_iter(&config, &mut self.niri.window_mru_ui, modifiers);\n                            let bind_left = find_configured_bind(\n                                bindings.clone(),\n                                mod_key,\n                                Trigger::WheelScrollLeft,\n                                mods,\n                            );\n                            let bind_right = find_configured_bind(\n                                bindings,\n                                mod_key,\n                                Trigger::WheelScrollRight,\n                                mods,\n                            );\n                            (bind_left, bind_right)\n                        };\n\n                    if let Some(right) = bind_right {\n                        for _ in 0..ticks {\n                            self.handle_bind(right.clone());\n                        }\n                    }\n                    if let Some(left) = bind_left {\n                        for _ in ticks..0 {\n                            self.handle_bind(left.clone());\n                        }\n                    }\n                }\n\n                let vertical = vertical_amount_v120.unwrap_or(0.);\n                let ticks = self.niri.vertical_wheel_tracker.accumulate(vertical);\n                if ticks != 0 {\n                    let (bind_up, bind_down) = if should_handle_in_overview && modifiers.is_empty()\n                    {\n                        let bind_up = Some(Bind {\n                            key: Key {\n                                trigger: Trigger::WheelScrollUp,\n                                modifiers: Modifiers::empty(),\n                            },\n                            action: Action::FocusWorkspaceUpUnderMouse,\n                            repeat: true,\n                            cooldown: Some(Duration::from_millis(50)),\n                            allow_when_locked: false,\n                            allow_inhibiting: false,\n                            hotkey_overlay_title: None,\n                        });\n                        let bind_down = Some(Bind {\n                            key: Key {\n                                trigger: Trigger::WheelScrollDown,\n                                modifiers: Modifiers::empty(),\n                            },\n                            action: Action::FocusWorkspaceDownUnderMouse,\n                            repeat: true,\n                            cooldown: Some(Duration::from_millis(50)),\n                            allow_when_locked: false,\n                            allow_inhibiting: false,\n                            hotkey_overlay_title: None,\n                        });\n                        (bind_up, bind_down)\n                    } else if should_handle_in_overview && modifiers == Modifiers::SHIFT {\n                        let bind_up = Some(Bind {\n                            key: Key {\n                                trigger: Trigger::WheelScrollUp,\n                                modifiers: Modifiers::empty(),\n                            },\n                            action: Action::FocusColumnLeftUnderMouse,\n                            repeat: true,\n                            cooldown: Some(Duration::from_millis(50)),\n                            allow_when_locked: false,\n                            allow_inhibiting: false,\n                            hotkey_overlay_title: None,\n                        });\n                        let bind_down = Some(Bind {\n                            key: Key {\n                                trigger: Trigger::WheelScrollDown,\n                                modifiers: Modifiers::empty(),\n                            },\n                            action: Action::FocusColumnRightUnderMouse,\n                            repeat: true,\n                            cooldown: Some(Duration::from_millis(50)),\n                            allow_when_locked: false,\n                            allow_inhibiting: false,\n                            hotkey_overlay_title: None,\n                        });\n                        (bind_up, bind_down)\n                    } else {\n                        let config = self.niri.config.borrow();\n                        let bindings =\n                            make_binds_iter(&config, &mut self.niri.window_mru_ui, modifiers);\n                        let bind_up = find_configured_bind(\n                            bindings.clone(),\n                            mod_key,\n                            Trigger::WheelScrollUp,\n                            mods,\n                        );\n                        let bind_down =\n                            find_configured_bind(bindings, mod_key, Trigger::WheelScrollDown, mods);\n                        (bind_up, bind_down)\n                    };\n\n                    if let Some(down) = bind_down {\n                        for _ in 0..ticks {\n                            self.handle_bind(down.clone());\n                        }\n                    }\n                    if let Some(up) = bind_up {\n                        for _ in ticks..0 {\n                            self.handle_bind(up.clone());\n                        }\n                    }\n                }\n\n                return;\n            } else {\n                self.niri.horizontal_wheel_tracker.reset();\n                self.niri.vertical_wheel_tracker.reset();\n            }\n        }\n\n        let horizontal_amount = event.amount(Axis::Horizontal);\n        let vertical_amount = event.amount(Axis::Vertical);\n\n        // Handle touchpad scroll bindings.\n        if source == AxisSource::Finger {\n            let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();\n            let modifiers = modifiers_from_state(mods);\n\n            let horizontal = horizontal_amount.unwrap_or(0.);\n            let vertical = vertical_amount.unwrap_or(0.);\n\n            if should_handle_in_overview && modifiers.is_empty() {\n                let mut redraw = false;\n\n                let action = self\n                    .niri\n                    .overview_scroll_swipe_gesture\n                    .update(horizontal, vertical);\n                let is_vertical = self.niri.overview_scroll_swipe_gesture.is_vertical();\n\n                if action.end() {\n                    if is_vertical {\n                        redraw |= self\n                            .niri\n                            .layout\n                            .workspace_switch_gesture_end(Some(true))\n                            .is_some();\n                    } else {\n                        redraw |= self\n                            .niri\n                            .layout\n                            .view_offset_gesture_end(Some(true))\n                            .is_some();\n                    }\n                } else {\n                    // Maybe begin, then update.\n                    if is_vertical {\n                        if action.begin() {\n                            if let Some(output) = self.niri.output_under_cursor() {\n                                self.niri\n                                    .layout\n                                    .workspace_switch_gesture_begin(&output, true);\n                                redraw = true;\n                            }\n                        }\n\n                        let res = self\n                            .niri\n                            .layout\n                            .workspace_switch_gesture_update(vertical, timestamp, true);\n                        if let Some(Some(_)) = res {\n                            redraw = true;\n                        }\n                    } else {\n                        if action.begin() {\n                            if let Some((output, ws)) = self.niri.workspace_under_cursor(true) {\n                                let ws_id = ws.id();\n                                let ws_idx =\n                                    self.niri.layout.find_workspace_by_id(ws_id).unwrap().0;\n\n                                self.niri.layout.view_offset_gesture_begin(\n                                    &output,\n                                    Some(ws_idx),\n                                    true,\n                                );\n                                redraw = true;\n                            }\n                        }\n\n                        let res = self\n                            .niri\n                            .layout\n                            .view_offset_gesture_update(horizontal, timestamp, true);\n                        if let Some(Some(_)) = res {\n                            redraw = true;\n                        }\n                    }\n                }\n\n                if redraw {\n                    self.niri.queue_redraw_all();\n                }\n\n                return;\n            } else {\n                let mut redraw = false;\n                if self.niri.overview_scroll_swipe_gesture.reset() {\n                    if self.niri.overview_scroll_swipe_gesture.is_vertical() {\n                        redraw |= self\n                            .niri\n                            .layout\n                            .workspace_switch_gesture_end(Some(true))\n                            .is_some();\n                    } else {\n                        redraw |= self\n                            .niri\n                            .layout\n                            .view_offset_gesture_end(Some(true))\n                            .is_some();\n                    }\n                }\n                if redraw {\n                    self.niri.queue_redraw_all();\n                }\n            }\n\n            if is_mru_open || self.niri.mods_with_finger_scroll_binds.contains(&modifiers) {\n                let ticks = self\n                    .niri\n                    .horizontal_finger_scroll_tracker\n                    .accumulate(horizontal);\n                if ticks != 0 {\n                    let config = self.niri.config.borrow();\n                    let bindings =\n                        make_binds_iter(&config, &mut self.niri.window_mru_ui, modifiers);\n                    let bind_left = find_configured_bind(\n                        bindings.clone(),\n                        mod_key,\n                        Trigger::TouchpadScrollLeft,\n                        mods,\n                    );\n                    let bind_right =\n                        find_configured_bind(bindings, mod_key, Trigger::TouchpadScrollRight, mods);\n                    drop(config);\n\n                    if let Some(right) = bind_right {\n                        for _ in 0..ticks {\n                            self.handle_bind(right.clone());\n                        }\n                    }\n                    if let Some(left) = bind_left {\n                        for _ in ticks..0 {\n                            self.handle_bind(left.clone());\n                        }\n                    }\n                }\n\n                let ticks = self\n                    .niri\n                    .vertical_finger_scroll_tracker\n                    .accumulate(vertical);\n                if ticks != 0 {\n                    let config = self.niri.config.borrow();\n                    let bindings =\n                        make_binds_iter(&config, &mut self.niri.window_mru_ui, modifiers);\n                    let bind_up = find_configured_bind(\n                        bindings.clone(),\n                        mod_key,\n                        Trigger::TouchpadScrollUp,\n                        mods,\n                    );\n                    let bind_down =\n                        find_configured_bind(bindings, mod_key, Trigger::TouchpadScrollDown, mods);\n                    drop(config);\n\n                    if let Some(down) = bind_down {\n                        for _ in 0..ticks {\n                            self.handle_bind(down.clone());\n                        }\n                    }\n                    if let Some(up) = bind_up {\n                        for _ in ticks..0 {\n                            self.handle_bind(up.clone());\n                        }\n                    }\n                }\n\n                return;\n            } else {\n                self.niri.horizontal_finger_scroll_tracker.reset();\n                self.niri.vertical_finger_scroll_tracker.reset();\n            }\n        }\n\n        self.update_pointer_contents();\n\n        let device_scroll_factor = {\n            let config = self.niri.config.borrow();\n            match source {\n                AxisSource::Wheel => config.input.mouse.scroll_factor,\n                AxisSource::Finger => config.input.touchpad.scroll_factor,\n                _ => None,\n            }\n        };\n\n        // Get window-specific scroll factor\n        let window_scroll_factor = pointer\n            .current_focus()\n            .map(|focused| self.niri.find_root_shell_surface(&focused))\n            .and_then(|root| self.niri.layout.find_window_and_output(&root).unzip().0)\n            .and_then(|window| window.rules().scroll_factor)\n            .unwrap_or(1.);\n\n        // Determine final scroll factors based on configuration\n        let (horizontal_factor, vertical_factor) = device_scroll_factor\n            .map(|x| x.h_v_factors())\n            .unwrap_or((1.0, 1.0));\n        let (horizontal_factor, vertical_factor) = (\n            horizontal_factor * window_scroll_factor,\n            vertical_factor * window_scroll_factor,\n        );\n\n        let horizontal_amount = horizontal_amount.unwrap_or_else(|| {\n            // Winit backend, discrete scrolling.\n            horizontal_amount_v120.unwrap_or(0.0) / 120. * 15.\n        }) * horizontal_factor;\n\n        let vertical_amount = vertical_amount.unwrap_or_else(|| {\n            // Winit backend, discrete scrolling.\n            vertical_amount_v120.unwrap_or(0.0) / 120. * 15.\n        }) * vertical_factor;\n\n        let horizontal_amount_v120 = horizontal_amount_v120.map(|x| x * horizontal_factor);\n        let vertical_amount_v120 = vertical_amount_v120.map(|x| x * vertical_factor);\n\n        let mut frame = AxisFrame::new(event.time_msec()).source(source);\n        if horizontal_amount != 0.0 {\n            frame = frame\n                .relative_direction(Axis::Horizontal, event.relative_direction(Axis::Horizontal));\n            frame = frame.value(Axis::Horizontal, horizontal_amount);\n            if let Some(v120) = horizontal_amount_v120 {\n                frame = frame.v120(Axis::Horizontal, v120 as i32);\n            }\n        }\n        if vertical_amount != 0.0 {\n            frame =\n                frame.relative_direction(Axis::Vertical, event.relative_direction(Axis::Vertical));\n            frame = frame.value(Axis::Vertical, vertical_amount);\n            if let Some(v120) = vertical_amount_v120 {\n                frame = frame.v120(Axis::Vertical, v120 as i32);\n            }\n        }\n\n        if source == AxisSource::Finger {\n            if event.amount(Axis::Horizontal) == Some(0.0) {\n                frame = frame.stop(Axis::Horizontal);\n            }\n            if event.amount(Axis::Vertical) == Some(0.0) {\n                frame = frame.stop(Axis::Vertical);\n            }\n        }\n\n        pointer.axis(self, frame);\n        pointer.frame(self);\n    }\n\n    fn on_tablet_tool_axis<I: InputBackend>(&mut self, event: I::TabletToolAxisEvent)\n    where\n        I::Device: 'static, // Needed for downcasting.\n    {\n        let Some(pos) = self.compute_tablet_position(&event) else {\n            return;\n        };\n\n        if let Some(output) = self.niri.screenshot_ui.selection_output() {\n            let geom = self.niri.global_space.output_geometry(output).unwrap();\n            let mut point = (pos - geom.loc.to_f64())\n                .to_physical(output.current_scale().fractional_scale())\n                .to_i32_round::<i32>();\n\n            let size = output.current_mode().unwrap().size;\n            let transform = output.current_transform();\n            let size = transform.transform_size(size);\n            point.x = point.x.clamp(0, size.w - 1);\n            point.y = point.y.clamp(0, size.h - 1);\n\n            self.niri.screenshot_ui.pointer_motion(point, None);\n        }\n\n        if let Some(mru_output) = self.niri.window_mru_ui.output() {\n            if let Some((output, pos_within_output)) = self.niri.output_under(pos) {\n                if mru_output == output {\n                    self.niri.window_mru_ui.pointer_motion(pos_within_output);\n                }\n            }\n        }\n\n        let under = self.niri.contents_under(pos);\n\n        let tablet_seat = self.niri.seat.tablet_seat();\n        let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device()));\n        let tool = tablet_seat.get_tool(&event.tool());\n        if let (Some(tablet), Some(tool)) = (tablet, tool) {\n            if event.pressure_has_changed() {\n                tool.pressure(event.pressure());\n            }\n            if event.distance_has_changed() {\n                tool.distance(event.distance());\n            }\n            if event.tilt_has_changed() {\n                tool.tilt(event.tilt());\n            }\n            if event.slider_has_changed() {\n                tool.slider_position(event.slider_position());\n            }\n            if event.rotation_has_changed() {\n                tool.rotation(event.rotation());\n            }\n            if event.wheel_has_changed() {\n                tool.wheel(event.wheel_delta(), event.wheel_delta_discrete());\n            }\n\n            tool.motion(\n                pos,\n                under.surface,\n                &tablet,\n                SERIAL_COUNTER.next_serial(),\n                event.time_msec(),\n            );\n\n            self.niri.pointer_visibility = PointerVisibility::Visible;\n            self.niri.tablet_cursor_location = Some(pos);\n        }\n\n        // Redraw to update the cursor position.\n        // FIXME: redraw only outputs overlapping the cursor.\n        self.niri.queue_redraw_all();\n    }\n\n    fn on_tablet_tool_tip<I: InputBackend>(&mut self, event: I::TabletToolTipEvent) {\n        let tool = self.niri.seat.tablet_seat().get_tool(&event.tool());\n\n        let Some(tool) = tool else {\n            return;\n        };\n        let tip_state = event.tip_state();\n\n        let is_overview_open = self.niri.layout.is_overview_open();\n\n        match tip_state {\n            TabletToolTipState::Down => {\n                let serial = SERIAL_COUNTER.next_serial();\n                tool.tip_down(serial, event.time_msec());\n\n                if let Some(pos) = self.niri.tablet_cursor_location {\n                    let under = self.niri.contents_under(pos);\n\n                    if self.niri.screenshot_ui.is_open() {\n                        if let Some(output) = under.output.clone() {\n                            let geom = self.niri.global_space.output_geometry(&output).unwrap();\n                            let mut point = (pos - geom.loc.to_f64())\n                                .to_physical(output.current_scale().fractional_scale())\n                                .to_i32_round();\n\n                            let size = output.current_mode().unwrap().size;\n                            let transform = output.current_transform();\n                            let size = transform.transform_size(size);\n                            point.x = min(size.w - 1, point.x);\n                            point.y = min(size.h - 1, point.y);\n\n                            if self.niri.screenshot_ui.pointer_down(output, point, None) {\n                                self.niri.queue_redraw_all();\n                            }\n                        }\n                    } else if let Some(mru_output) = self.niri.window_mru_ui.output() {\n                        if let Some((output, pos_within_output)) = self.niri.output_under(pos) {\n                            if mru_output == output {\n                                let id = self.niri.window_mru_ui.pointer_motion(pos_within_output);\n                                if id.is_some() {\n                                    self.confirm_mru();\n                                } else {\n                                    self.niri.cancel_mru();\n                                }\n                            } else {\n                                self.niri.cancel_mru();\n                            }\n                        }\n                    } else if let Some((window, _)) = under.window {\n                        if let Some(output) = is_overview_open.then_some(under.output).flatten() {\n                            let mut workspaces = self.niri.layout.workspaces();\n                            if let Some(ws_idx) = workspaces.find_map(|(_, ws_idx, ws)| {\n                                ws.windows().any(|w| w.window == window).then_some(ws_idx)\n                            }) {\n                                drop(workspaces);\n                                self.niri.layout.focus_output(&output);\n                                self.niri.layout.toggle_overview_to_workspace(ws_idx);\n                            }\n                        }\n\n                        self.niri.layout.activate_window(&window);\n\n                        // FIXME: granular.\n                        self.niri.queue_redraw_all();\n                    } else if let Some((output, ws)) = is_overview_open\n                        .then(|| self.niri.workspace_under(false, pos))\n                        .flatten()\n                    {\n                        let ws_idx = self.niri.layout.find_workspace_by_id(ws.id()).unwrap().0;\n\n                        self.niri.layout.focus_output(&output);\n                        self.niri.layout.toggle_overview_to_workspace(ws_idx);\n\n                        // FIXME: granular.\n                        self.niri.queue_redraw_all();\n                    } else if let Some(output) = under.output {\n                        self.niri.layout.focus_output(&output);\n\n                        // FIXME: granular.\n                        self.niri.queue_redraw_all();\n                    }\n                    self.niri.focus_layer_surface_if_on_demand(under.layer);\n                }\n            }\n            TabletToolTipState::Up => {\n                if let Some(capture) = self.niri.screenshot_ui.pointer_up(None) {\n                    if capture {\n                        self.confirm_screenshot(true);\n                    } else {\n                        self.niri.queue_redraw_all();\n                    }\n                }\n\n                tool.tip_up(event.time_msec());\n            }\n        }\n    }\n\n    fn on_tablet_tool_proximity<I: InputBackend>(&mut self, event: I::TabletToolProximityEvent)\n    where\n        I::Device: 'static, // Needed for downcasting.\n    {\n        let Some(pos) = self.compute_tablet_position(&event) else {\n            return;\n        };\n\n        let under = self.niri.contents_under(pos);\n\n        let tablet_seat = self.niri.seat.tablet_seat();\n        let display_handle = self.niri.display_handle.clone();\n        let tool = tablet_seat.add_tool::<Self>(self, &display_handle, &event.tool());\n        let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device()));\n        if let Some(tablet) = tablet {\n            match event.state() {\n                ProximityState::In => {\n                    if let Some(under) = under.surface {\n                        tool.proximity_in(\n                            pos,\n                            under,\n                            &tablet,\n                            SERIAL_COUNTER.next_serial(),\n                            event.time_msec(),\n                        );\n                    }\n                    self.niri.pointer_visibility = PointerVisibility::Visible;\n                    self.niri.tablet_cursor_location = Some(pos);\n                }\n                ProximityState::Out => {\n                    tool.proximity_out(event.time_msec());\n\n                    // Move the mouse pointer here to avoid discontinuity.\n                    //\n                    // Plus, Wayland SDL2 currently warps the pointer into some weird\n                    // location on proximity out, so this should help it a little.\n                    if let Some(pos) = self.niri.tablet_cursor_location {\n                        self.move_cursor(pos);\n                    }\n\n                    self.niri.pointer_visibility = PointerVisibility::Visible;\n                    self.niri.tablet_cursor_location = None;\n                }\n            }\n\n            // FIXME: granular.\n            self.niri.queue_redraw_all();\n        }\n    }\n\n    fn on_tablet_tool_button<I: InputBackend>(&mut self, event: I::TabletToolButtonEvent) {\n        let tool = self.niri.seat.tablet_seat().get_tool(&event.tool());\n\n        if let Some(tool) = tool {\n            tool.button(\n                event.button(),\n                event.button_state(),\n                SERIAL_COUNTER.next_serial(),\n                event.time_msec(),\n            );\n        }\n    }\n\n    fn on_gesture_swipe_begin<I: InputBackend>(&mut self, event: I::GestureSwipeBeginEvent) {\n        if self.niri.window_mru_ui.is_open() {\n            // Don't start swipe gestures while in the MRU.\n            return;\n        }\n\n        if event.fingers() == 3 {\n            self.niri.gesture_swipe_3f_cumulative = Some((0., 0.));\n\n            // We handled this event.\n            return;\n        } else if event.fingers() == 4 {\n            self.niri.layout.overview_gesture_begin();\n            self.niri.queue_redraw_all();\n\n            // We handled this event.\n            return;\n        }\n\n        let serial = SERIAL_COUNTER.next_serial();\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_swipe_begin(\n            self,\n            &GestureSwipeBeginEvent {\n                serial,\n                time: event.time_msec(),\n                fingers: event.fingers(),\n            },\n        );\n    }\n\n    fn on_gesture_swipe_update<I: InputBackend + 'static>(\n        &mut self,\n        event: I::GestureSwipeUpdateEvent,\n    ) where\n        I::Device: 'static,\n    {\n        let mut delta_x = event.delta_x();\n        let mut delta_y = event.delta_y();\n\n        if let Some(libinput_event) =\n            (&event as &dyn Any).downcast_ref::<input::event::gesture::GestureSwipeUpdateEvent>()\n        {\n            delta_x = libinput_event.dx_unaccelerated();\n            delta_y = libinput_event.dy_unaccelerated();\n        }\n\n        let uninverted_delta_y = delta_y;\n\n        let device = event.device();\n        if let Some(device) = (&device as &dyn Any).downcast_ref::<input::Device>() {\n            if device.config_scroll_natural_scroll_enabled() {\n                delta_x = -delta_x;\n                delta_y = -delta_y;\n            }\n        }\n\n        let is_overview_open = self.niri.layout.is_overview_open();\n\n        if let Some((cx, cy)) = &mut self.niri.gesture_swipe_3f_cumulative {\n            *cx += delta_x;\n            *cy += delta_y;\n\n            // Check if the gesture moved far enough to decide. Threshold copied from GNOME Shell.\n            let (cx, cy) = (*cx, *cy);\n            if cx * cx + cy * cy >= 16. * 16. {\n                self.niri.gesture_swipe_3f_cumulative = None;\n\n                if let Some(output) = self.niri.output_under_cursor() {\n                    if cx.abs() > cy.abs() {\n                        let output_ws = if is_overview_open {\n                            self.niri.workspace_under_cursor(true)\n                        } else {\n                            // We don't want to accidentally \"catch\" the wrong workspace during\n                            // animations.\n                            self.niri.output_under_cursor().and_then(|output| {\n                                let mon = self.niri.layout.monitor_for_output(&output)?;\n                                Some((output, mon.active_workspace_ref()))\n                            })\n                        };\n\n                        if let Some((output, ws)) = output_ws {\n                            let ws_idx = self.niri.layout.find_workspace_by_id(ws.id()).unwrap().0;\n                            self.niri\n                                .layout\n                                .view_offset_gesture_begin(&output, Some(ws_idx), true);\n                        }\n                    } else {\n                        self.niri\n                            .layout\n                            .workspace_switch_gesture_begin(&output, true);\n                    }\n                }\n            }\n        }\n\n        let timestamp = Duration::from_micros(event.time());\n\n        let mut handled = false;\n        let res = self\n            .niri\n            .layout\n            .workspace_switch_gesture_update(delta_y, timestamp, true);\n        if let Some(output) = res {\n            if let Some(output) = output {\n                self.niri.queue_redraw(&output);\n            }\n            handled = true;\n        }\n\n        let res = self\n            .niri\n            .layout\n            .view_offset_gesture_update(delta_x, timestamp, true);\n        if let Some(output) = res {\n            if let Some(output) = output {\n                self.niri.queue_redraw(&output);\n            }\n            handled = true;\n        }\n\n        let res = self\n            .niri\n            .layout\n            .overview_gesture_update(-uninverted_delta_y, timestamp);\n        if let Some(redraw) = res {\n            if redraw {\n                self.niri.queue_redraw_all();\n            }\n            handled = true;\n        }\n\n        if handled {\n            // We handled this event.\n            return;\n        }\n\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_swipe_update(\n            self,\n            &GestureSwipeUpdateEvent {\n                time: event.time_msec(),\n                delta: event.delta(),\n            },\n        );\n    }\n\n    fn on_gesture_swipe_end<I: InputBackend>(&mut self, event: I::GestureSwipeEndEvent) {\n        self.niri.gesture_swipe_3f_cumulative = None;\n\n        let mut handled = false;\n        let res = self.niri.layout.workspace_switch_gesture_end(Some(true));\n        if let Some(output) = res {\n            self.niri.queue_redraw(&output);\n            handled = true;\n        }\n\n        let res = self.niri.layout.view_offset_gesture_end(Some(true));\n        if let Some(output) = res {\n            self.niri.queue_redraw(&output);\n            handled = true;\n        }\n\n        let res = self.niri.layout.overview_gesture_end();\n        if res {\n            self.niri.queue_redraw_all();\n            handled = true;\n        }\n\n        if handled {\n            // We handled this event.\n            return;\n        }\n\n        let serial = SERIAL_COUNTER.next_serial();\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_swipe_end(\n            self,\n            &GestureSwipeEndEvent {\n                serial,\n                time: event.time_msec(),\n                cancelled: event.cancelled(),\n            },\n        );\n    }\n\n    fn on_gesture_pinch_begin<I: InputBackend>(&mut self, event: I::GesturePinchBeginEvent) {\n        let serial = SERIAL_COUNTER.next_serial();\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_pinch_begin(\n            self,\n            &GesturePinchBeginEvent {\n                serial,\n                time: event.time_msec(),\n                fingers: event.fingers(),\n            },\n        );\n    }\n\n    fn on_gesture_pinch_update<I: InputBackend>(&mut self, event: I::GesturePinchUpdateEvent) {\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_pinch_update(\n            self,\n            &GesturePinchUpdateEvent {\n                time: event.time_msec(),\n                delta: event.delta(),\n                scale: event.scale(),\n                rotation: event.rotation(),\n            },\n        );\n    }\n\n    fn on_gesture_pinch_end<I: InputBackend>(&mut self, event: I::GesturePinchEndEvent) {\n        let serial = SERIAL_COUNTER.next_serial();\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_pinch_end(\n            self,\n            &GesturePinchEndEvent {\n                serial,\n                time: event.time_msec(),\n                cancelled: event.cancelled(),\n            },\n        );\n    }\n\n    fn on_gesture_hold_begin<I: InputBackend>(&mut self, event: I::GestureHoldBeginEvent) {\n        let serial = SERIAL_COUNTER.next_serial();\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_hold_begin(\n            self,\n            &GestureHoldBeginEvent {\n                serial,\n                time: event.time_msec(),\n                fingers: event.fingers(),\n            },\n        );\n    }\n\n    fn on_gesture_hold_end<I: InputBackend>(&mut self, event: I::GestureHoldEndEvent) {\n        let serial = SERIAL_COUNTER.next_serial();\n        let pointer = self.niri.seat.get_pointer().unwrap();\n\n        if self.update_pointer_contents() {\n            pointer.frame(self);\n        }\n\n        pointer.gesture_hold_end(\n            self,\n            &GestureHoldEndEvent {\n                serial,\n                time: event.time_msec(),\n                cancelled: event.cancelled(),\n            },\n        );\n    }\n\n    fn compute_absolute_location<I: InputBackend>(\n        &self,\n        evt: &impl AbsolutePositionEvent<I>,\n        fallback_output: Option<&Output>,\n    ) -> Option<Point<f64, Logical>> {\n        let output = evt.device().output(self);\n        let output = output.as_ref().or(fallback_output)?;\n        let output_geo = self.niri.global_space.output_geometry(output).unwrap();\n        let transform = output.current_transform();\n        let size = transform.invert().transform_size(output_geo.size);\n        Some(\n            transform.transform_point_in(evt.position_transformed(size), &size.to_f64())\n                + output_geo.loc.to_f64(),\n        )\n    }\n\n    /// Computes the cursor position for the touch event.\n    ///\n    /// This function handles the touch output mapping, as well as coordinate transform\n    fn compute_touch_location<I: InputBackend>(\n        &self,\n        evt: &impl AbsolutePositionEvent<I>,\n    ) -> Option<Point<f64, Logical>> {\n        self.compute_absolute_location(evt, self.niri.output_for_touch())\n    }\n\n    fn on_touch_down<I: InputBackend>(&mut self, evt: I::TouchDownEvent) {\n        let Some(handle) = self.niri.seat.get_touch() else {\n            return;\n        };\n        let Some(pos) = self.compute_touch_location(&evt) else {\n            return;\n        };\n        let slot = evt.slot();\n\n        let serial = SERIAL_COUNTER.next_serial();\n\n        let under = self.niri.contents_under(pos);\n\n        let mod_key = self.backend.mod_key(&self.niri.config.borrow());\n\n        if self.niri.screenshot_ui.is_open() {\n            if let Some(output) = under.output.clone() {\n                let geom = self.niri.global_space.output_geometry(&output).unwrap();\n                let mut point = (pos - geom.loc.to_f64())\n                    .to_physical(output.current_scale().fractional_scale())\n                    .to_i32_round();\n\n                let size = output.current_mode().unwrap().size;\n                let transform = output.current_transform();\n                let size = transform.transform_size(size);\n                point.x = min(size.w - 1, point.x);\n                point.y = min(size.h - 1, point.y);\n\n                if self\n                    .niri\n                    .screenshot_ui\n                    .pointer_down(output, point, Some(slot))\n                {\n                    self.niri.queue_redraw_all();\n                }\n            }\n        } else if let Some(mru_output) = self.niri.window_mru_ui.output() {\n            if let Some((output, pos_within_output)) = self.niri.output_under(pos) {\n                if mru_output == output {\n                    let id = self.niri.window_mru_ui.pointer_motion(pos_within_output);\n                    if id.is_some() {\n                        self.confirm_mru();\n                    } else {\n                        self.niri.cancel_mru();\n                    }\n                } else {\n                    self.niri.cancel_mru();\n                }\n            }\n        } else if !handle.is_grabbed() {\n            let mods = self.niri.seat.get_keyboard().unwrap().modifier_state();\n            let mods = modifiers_from_state(mods);\n            let mod_down = mods.contains(mod_key.to_modifiers());\n\n            if self.niri.layout.is_overview_open()\n                && !mod_down\n                && under.layer.is_none()\n                && under.output.is_some()\n            {\n                let (output, pos_within_output) = self.niri.output_under(pos).unwrap();\n                let output = output.clone();\n\n                let mut matched_narrow = true;\n                let mut ws = self.niri.workspace_under(false, pos);\n                if ws.is_none() {\n                    matched_narrow = false;\n                    ws = self.niri.workspace_under(true, pos);\n                }\n                let ws_id = ws.map(|(_, ws)| ws.id());\n\n                let mapped = self.niri.window_under(pos);\n                let window = mapped.map(|mapped| mapped.window.clone());\n\n                let start_data = TouchGrabStartData {\n                    focus: None,\n                    slot,\n                    location: pos,\n                };\n                let start_timestamp = Duration::from_micros(evt.time());\n                let grab = TouchOverviewGrab::new(\n                    start_data,\n                    start_timestamp,\n                    output,\n                    pos_within_output,\n                    ws_id,\n                    matched_narrow,\n                    window,\n                );\n                handle.set_grab(self, grab, serial);\n            } else if let Some((window, _)) = under.window {\n                self.niri.layout.activate_window(&window);\n\n                // Check if we need to start a touch move grab.\n                if mod_down {\n                    let start_data = TouchGrabStartData {\n                        focus: None,\n                        slot,\n                        location: pos,\n                    };\n                    let start_data = PointerOrTouchStartData::Touch(start_data);\n                    if let Some(grab) = MoveGrab::new(self, start_data, window.clone(), true, None)\n                    {\n                        handle.set_grab(self, grab, serial);\n                    }\n                }\n\n                // FIXME: granular.\n                self.niri.queue_redraw_all();\n            } else if let Some(output) = under.output {\n                self.niri.layout.focus_output(&output);\n\n                // FIXME: granular.\n                self.niri.queue_redraw_all();\n            }\n            self.niri.focus_layer_surface_if_on_demand(under.layer);\n        };\n\n        handle.down(\n            self,\n            under.surface,\n            &DownEvent {\n                slot,\n                location: pos,\n                serial,\n                time: evt.time_msec(),\n            },\n        );\n\n        // We're using touch, hide the pointer.\n        self.niri.pointer_visibility = PointerVisibility::Disabled;\n    }\n    fn on_touch_up<I: InputBackend>(&mut self, evt: I::TouchUpEvent) {\n        let Some(handle) = self.niri.seat.get_touch() else {\n            return;\n        };\n        let slot = evt.slot();\n\n        if let Some(capture) = self.niri.screenshot_ui.pointer_up(Some(slot)) {\n            if capture {\n                self.confirm_screenshot(true);\n            } else {\n                self.niri.queue_redraw_all();\n            }\n        }\n\n        let serial = SERIAL_COUNTER.next_serial();\n        handle.up(\n            self,\n            &UpEvent {\n                slot,\n                serial,\n                time: evt.time_msec(),\n            },\n        )\n    }\n    fn on_touch_motion<I: InputBackend>(&mut self, evt: I::TouchMotionEvent) {\n        let Some(handle) = self.niri.seat.get_touch() else {\n            return;\n        };\n        let Some(pos) = self.compute_touch_location(&evt) else {\n            return;\n        };\n        let slot = evt.slot();\n\n        if let Some(output) = self.niri.screenshot_ui.selection_output().cloned() {\n            let geom = self.niri.global_space.output_geometry(&output).unwrap();\n            let mut point = (pos - geom.loc.to_f64())\n                .to_physical(output.current_scale().fractional_scale())\n                .to_i32_round::<i32>();\n\n            let size = output.current_mode().unwrap().size;\n            let transform = output.current_transform();\n            let size = transform.transform_size(size);\n            point.x = point.x.clamp(0, size.w - 1);\n            point.y = point.y.clamp(0, size.h - 1);\n\n            self.niri.screenshot_ui.pointer_motion(point, Some(slot));\n            self.niri.queue_redraw(&output);\n        }\n\n        let under = self.niri.contents_under(pos);\n        handle.motion(\n            self,\n            under.surface,\n            &TouchMotionEvent {\n                slot,\n                location: pos,\n                time: evt.time_msec(),\n            },\n        );\n\n        // Inform the layout of an ongoing DnD operation.\n        let is_dnd_grab = handle\n            .with_grab(|_, grab| Self::is_dnd_grab(grab.as_any()))\n            .unwrap_or(false);\n        if is_dnd_grab {\n            if let Some((output, pos_within_output)) = self.niri.output_under(pos) {\n                let output = output.clone();\n                self.niri.layout.dnd_update(output, pos_within_output);\n            }\n        }\n    }\n    fn on_touch_frame<I: InputBackend>(&mut self, _evt: I::TouchFrameEvent) {\n        let Some(handle) = self.niri.seat.get_touch() else {\n            return;\n        };\n        handle.frame(self);\n    }\n    fn on_touch_cancel<I: InputBackend>(&mut self, _evt: I::TouchCancelEvent) {\n        let Some(handle) = self.niri.seat.get_touch() else {\n            return;\n        };\n        handle.cancel(self);\n    }\n\n    fn on_switch_toggle<I: InputBackend>(&mut self, evt: I::SwitchToggleEvent) {\n        let Some(switch) = evt.switch() else {\n            return;\n        };\n\n        if switch == Switch::Lid {\n            let is_closed = evt.state() == SwitchState::On;\n            trace!(\"lid switch {}\", if is_closed { \"closed\" } else { \"opened\" });\n            self.set_lid_closed(is_closed);\n        }\n\n        let action = {\n            let bindings = &self.niri.config.borrow().switch_events;\n            find_configured_switch_action(bindings, switch, evt.state())\n        };\n\n        if let Some(action) = action {\n            self.do_action(action, true);\n        }\n    }\n\n    pub fn is_dnd_grab(grab: &dyn Any) -> bool {\n        // Normal DnD\n        grab.is::<DnDGrab<Self, WlDataSource, WlSurface>>()\n            // Null-source DnD: weston-dnd --self-only\n            || grab.is::<DnDGrab<Self, WlSurface, WlSurface>>()\n    }\n}\n\n/// Check whether the key should be intercepted and mark intercepted\n/// pressed keys as `suppressed`, thus preventing `releases` corresponding\n/// to them from being delivered.\n#[allow(clippy::too_many_arguments)]\nfn should_intercept_key<'a>(\n    suppressed_keys: &mut HashSet<Keycode>,\n    bindings: impl IntoIterator<Item = &'a Bind>,\n    mod_key: ModKey,\n    key_code: Keycode,\n    modified: Keysym,\n    raw: Option<Keysym>,\n    pressed: bool,\n    mods: ModifiersState,\n    screenshot_ui: &ScreenshotUi,\n    disable_power_key_handling: bool,\n    is_inhibiting_shortcuts: bool,\n) -> FilterResult<Option<Bind>> {\n    // Actions are only triggered on presses, release of the key\n    // shouldn't try to intercept anything unless we have marked\n    // the key to suppress.\n    if !pressed && !suppressed_keys.contains(&key_code) {\n        return FilterResult::Forward;\n    }\n\n    let mut final_bind = find_bind(\n        bindings,\n        mod_key,\n        modified,\n        raw,\n        mods,\n        disable_power_key_handling,\n    );\n\n    // Allow only a subset of compositor actions while the screenshot UI is open, since the user\n    // cannot see the screen.\n    if screenshot_ui.is_open() {\n        let mut use_screenshot_ui_action = true;\n\n        if let Some(bind) = &final_bind {\n            if allowed_during_screenshot(&bind.action) {\n                use_screenshot_ui_action = false;\n            }\n        }\n\n        if use_screenshot_ui_action {\n            if let Some(raw) = raw {\n                final_bind = screenshot_ui.action(raw, mods).map(|action| Bind {\n                    key: Key {\n                        trigger: Trigger::Keysym(raw),\n                        // Not entirely correct but it doesn't matter in how we currently use it.\n                        modifiers: Modifiers::empty(),\n                    },\n                    action,\n                    repeat: true,\n                    cooldown: None,\n                    allow_when_locked: false,\n                    // The screenshot UI owns the focus anyway, so this doesn't really matter.\n                    // But logically, nothing can inhibit its actions. Only opening it can be\n                    // inhibited.\n                    allow_inhibiting: false,\n                    hotkey_overlay_title: None,\n                });\n            }\n        }\n    }\n\n    match (final_bind, pressed) {\n        (Some(bind), true) => {\n            if is_inhibiting_shortcuts && bind.allow_inhibiting {\n                FilterResult::Forward\n            } else {\n                suppressed_keys.insert(key_code);\n                FilterResult::Intercept(Some(bind))\n            }\n        }\n        (_, false) => {\n            // By this point, we know that the key was suppressed on press. Even if we're inhibiting\n            // shortcuts, we should still suppress the release.\n            // But we don't need to check for shortcuts inhibition here, because\n            // if it was inhibited on press (forwarded to the client), it wouldn't be suppressed,\n            // so the release would already have been forwarded at the start of this function.\n            suppressed_keys.remove(&key_code);\n            FilterResult::Intercept(None)\n        }\n        (None, true) => FilterResult::Forward,\n    }\n}\n\nfn find_bind<'a>(\n    bindings: impl IntoIterator<Item = &'a Bind>,\n    mod_key: ModKey,\n    modified: Keysym,\n    raw: Option<Keysym>,\n    mods: ModifiersState,\n    disable_power_key_handling: bool,\n) -> Option<Bind> {\n    use keysyms::*;\n\n    // Handle hardcoded binds.\n    #[allow(non_upper_case_globals)] // wat\n    let hardcoded_action = match modified.raw() {\n        modified @ KEY_XF86Switch_VT_1..=KEY_XF86Switch_VT_12 => {\n            let vt = (modified - KEY_XF86Switch_VT_1 + 1) as i32;\n            Some(Action::ChangeVt(vt))\n        }\n        KEY_XF86PowerOff if !disable_power_key_handling => Some(Action::Suspend),\n        _ => None,\n    };\n\n    if let Some(action) = hardcoded_action {\n        return Some(Bind {\n            key: Key {\n                // Not entirely correct but it doesn't matter in how we currently use it.\n                trigger: Trigger::Keysym(modified),\n                modifiers: Modifiers::empty(),\n            },\n            action,\n            repeat: true,\n            cooldown: None,\n            allow_when_locked: false,\n            // In a worst-case scenario, the user has no way to unlock the compositor and a\n            // misbehaving client has a keyboard shortcuts inhibitor, \"jailing\" the user.\n            // The user must always be able to change VTs to recover from such a situation.\n            // It also makes no sense to inhibit the default power key handling.\n            // Hardcoded binds must never be inhibited.\n            allow_inhibiting: false,\n            hotkey_overlay_title: None,\n        });\n    }\n\n    let trigger = Trigger::Keysym(raw?);\n    find_configured_bind(bindings, mod_key, trigger, mods)\n}\n\nfn find_configured_bind<'a>(\n    bindings: impl IntoIterator<Item = &'a Bind>,\n    mod_key: ModKey,\n    trigger: Trigger,\n    mods: ModifiersState,\n) -> Option<Bind> {\n    // Handle configured binds.\n    let mut modifiers = modifiers_from_state(mods);\n\n    let mod_down = modifiers_from_state(mods).contains(mod_key.to_modifiers());\n    if mod_down {\n        modifiers |= Modifiers::COMPOSITOR;\n    }\n\n    for bind in bindings {\n        if bind.key.trigger != trigger {\n            continue;\n        }\n\n        let mut bind_modifiers = bind.key.modifiers;\n        if bind_modifiers.contains(Modifiers::COMPOSITOR) {\n            bind_modifiers |= mod_key.to_modifiers();\n        } else if bind_modifiers.contains(mod_key.to_modifiers()) {\n            bind_modifiers |= Modifiers::COMPOSITOR;\n        }\n\n        if bind_modifiers == modifiers {\n            return Some(bind.clone());\n        }\n    }\n\n    None\n}\n\nfn find_configured_switch_action(\n    bindings: &SwitchBinds,\n    switch: Switch,\n    state: SwitchState,\n) -> Option<Action> {\n    let switch_action = match (switch, state) {\n        (Switch::Lid, SwitchState::Off) => &bindings.lid_open,\n        (Switch::Lid, SwitchState::On) => &bindings.lid_close,\n        (Switch::TabletMode, SwitchState::Off) => &bindings.tablet_mode_off,\n        (Switch::TabletMode, SwitchState::On) => &bindings.tablet_mode_on,\n        _ => unreachable!(),\n    };\n    switch_action\n        .as_ref()\n        .map(|switch_action| Action::Spawn(switch_action.spawn.clone()))\n}\n\nfn modifiers_from_state(mods: ModifiersState) -> Modifiers {\n    let mut modifiers = Modifiers::empty();\n    if mods.ctrl {\n        modifiers |= Modifiers::CTRL;\n    }\n    if mods.shift {\n        modifiers |= Modifiers::SHIFT;\n    }\n    if mods.alt {\n        modifiers |= Modifiers::ALT;\n    }\n    if mods.logo {\n        modifiers |= Modifiers::SUPER;\n    }\n    if mods.iso_level3_shift {\n        modifiers |= Modifiers::ISO_LEVEL3_SHIFT;\n    }\n    if mods.iso_level5_shift {\n        modifiers |= Modifiers::ISO_LEVEL5_SHIFT;\n    }\n    modifiers\n}\n\nfn should_activate_monitors<I: InputBackend>(event: &InputEvent<I>) -> bool {\n    match event {\n        InputEvent::Keyboard { event } if event.state() == KeyState::Pressed => true,\n        InputEvent::PointerButton { event } if event.state() == ButtonState::Pressed => true,\n        InputEvent::PointerMotion { .. }\n        | InputEvent::PointerMotionAbsolute { .. }\n        | InputEvent::PointerAxis { .. }\n        | InputEvent::GestureSwipeBegin { .. }\n        | InputEvent::GesturePinchBegin { .. }\n        | InputEvent::GestureHoldBegin { .. }\n        | InputEvent::TouchDown { .. }\n        | InputEvent::TouchMotion { .. }\n        | InputEvent::TabletToolAxis { .. }\n        | InputEvent::TabletToolProximity { .. }\n        | InputEvent::TabletToolTip { .. }\n        | InputEvent::TabletToolButton { .. } => true,\n        // Ignore events like device additions and removals, key releases, gesture ends.\n        _ => false,\n    }\n}\n\nfn should_hide_hotkey_overlay<I: InputBackend>(event: &InputEvent<I>) -> bool {\n    match event {\n        InputEvent::Keyboard { event } if event.state() == KeyState::Pressed => true,\n        InputEvent::PointerButton { event } if event.state() == ButtonState::Pressed => true,\n        InputEvent::PointerAxis { .. }\n        | InputEvent::GestureSwipeBegin { .. }\n        | InputEvent::GesturePinchBegin { .. }\n        | InputEvent::TouchDown { .. }\n        | InputEvent::TouchMotion { .. }\n        | InputEvent::TabletToolTip { .. }\n        | InputEvent::TabletToolButton { .. } => true,\n        _ => false,\n    }\n}\n\nfn should_hide_exit_confirm_dialog<I: InputBackend>(event: &InputEvent<I>) -> bool {\n    match event {\n        InputEvent::Keyboard { event } if event.state() == KeyState::Pressed => true,\n        InputEvent::PointerButton { event } if event.state() == ButtonState::Pressed => true,\n        InputEvent::PointerAxis { .. }\n        | InputEvent::GestureSwipeBegin { .. }\n        | InputEvent::GesturePinchBegin { .. }\n        | InputEvent::TouchDown { .. }\n        | InputEvent::TouchMotion { .. }\n        | InputEvent::TabletToolTip { .. }\n        | InputEvent::TabletToolButton { .. } => true,\n        _ => false,\n    }\n}\n\nfn should_notify_activity<I: InputBackend>(event: &InputEvent<I>) -> bool {\n    !matches!(\n        event,\n        InputEvent::DeviceAdded { .. } | InputEvent::DeviceRemoved { .. }\n    )\n}\n\nfn should_reset_pointer_inactivity_timer<I: InputBackend>(event: &InputEvent<I>) -> bool {\n    matches!(\n        event,\n        InputEvent::PointerAxis { .. }\n            | InputEvent::PointerButton { .. }\n            | InputEvent::PointerMotion { .. }\n            | InputEvent::PointerMotionAbsolute { .. }\n            | InputEvent::TabletToolAxis { .. }\n            | InputEvent::TabletToolButton { .. }\n            | InputEvent::TabletToolProximity { .. }\n            | InputEvent::TabletToolTip { .. }\n    )\n}\n\nfn allowed_when_locked(action: &Action) -> bool {\n    matches!(\n        action,\n        Action::Quit(_)\n            | Action::ChangeVt(_)\n            | Action::Suspend\n            | Action::PowerOffMonitors\n            | Action::PowerOnMonitors\n            | Action::SwitchLayout(_)\n            | Action::ToggleKeyboardShortcutsInhibit\n    )\n}\n\nfn allowed_during_screenshot(action: &Action) -> bool {\n    matches!(\n        action,\n        Action::Quit(_)\n            | Action::ChangeVt(_)\n            | Action::Suspend\n            | Action::PowerOffMonitors\n            | Action::PowerOnMonitors\n            // The screenshot UI can handle these.\n            | Action::MoveColumnLeft\n            | Action::MoveColumnLeftOrToMonitorLeft\n            | Action::MoveColumnRight\n            | Action::MoveColumnRightOrToMonitorRight\n            | Action::MoveWindowUp\n            | Action::MoveWindowUpOrToWorkspaceUp\n            | Action::MoveWindowDown\n            | Action::MoveWindowDownOrToWorkspaceDown\n            | Action::MoveColumnToMonitorLeft\n            | Action::MoveColumnToMonitorRight\n            | Action::MoveColumnToMonitorUp\n            | Action::MoveColumnToMonitorDown\n            | Action::MoveColumnToMonitorPrevious\n            | Action::MoveColumnToMonitorNext\n            | Action::MoveColumnToMonitor(_)\n            | Action::MoveWindowToMonitorLeft\n            | Action::MoveWindowToMonitorRight\n            | Action::MoveWindowToMonitorUp\n            | Action::MoveWindowToMonitorDown\n            | Action::MoveWindowToMonitorPrevious\n            | Action::MoveWindowToMonitorNext\n            | Action::MoveWindowToMonitor(_)\n            | Action::SetWindowWidth(_)\n            | Action::SetWindowHeight(_)\n            | Action::SetColumnWidth(_)\n    )\n}\n\nfn hardcoded_overview_bind(raw: Keysym, mods: ModifiersState) -> Option<Bind> {\n    let mods = modifiers_from_state(mods);\n    if !mods.is_empty() {\n        return None;\n    }\n\n    let mut repeat = true;\n    let action = match raw {\n        Keysym::Escape | Keysym::Return => {\n            repeat = false;\n            Action::ToggleOverview\n        }\n        Keysym::Left => Action::FocusColumnLeft,\n        Keysym::Right => Action::FocusColumnRight,\n        Keysym::Up => Action::FocusWindowOrWorkspaceUp,\n        Keysym::Down => Action::FocusWindowOrWorkspaceDown,\n        _ => {\n            return None;\n        }\n    };\n\n    Some(Bind {\n        key: Key {\n            trigger: Trigger::Keysym(raw),\n            modifiers: Modifiers::empty(),\n        },\n        action,\n        repeat,\n        cooldown: None,\n        allow_when_locked: false,\n        allow_inhibiting: false,\n        hotkey_overlay_title: None,\n    })\n}\n\npub fn apply_libinput_settings(config: &niri_config::Input, device: &mut input::Device) {\n    // According to Mutter code, this setting is specific to touchpads.\n    let is_touchpad = device.config_tap_finger_count() > 0;\n    if is_touchpad {\n        let c = &config.touchpad;\n        let _ = device.config_send_events_set_mode(if c.off {\n            input::SendEventsMode::DISABLED\n        } else if c.disabled_on_external_mouse {\n            input::SendEventsMode::DISABLED_ON_EXTERNAL_MOUSE\n        } else {\n            input::SendEventsMode::ENABLED\n        });\n        let _ = device.config_tap_set_enabled(c.tap);\n        let _ = device.config_dwt_set_enabled(c.dwt);\n        let _ = device.config_dwtp_set_enabled(c.dwtp);\n        let _ = device.config_tap_set_drag_lock_enabled(c.drag_lock);\n        let _ = device.config_scroll_set_natural_scroll_enabled(c.natural_scroll);\n        let _ = device.config_accel_set_speed(c.accel_speed.0);\n        let _ = device.config_left_handed_set(c.left_handed);\n        let _ = device.config_middle_emulation_set_enabled(c.middle_emulation);\n\n        if let Some(drag) = c.drag {\n            let _ = device.config_tap_set_drag_enabled(drag);\n        } else {\n            let default = device.config_tap_default_drag_enabled();\n            let _ = device.config_tap_set_drag_enabled(default);\n        }\n\n        if let Some(accel_profile) = c.accel_profile {\n            let _ = device.config_accel_set_profile(accel_profile.into());\n        } else if let Some(default) = device.config_accel_default_profile() {\n            let _ = device.config_accel_set_profile(default);\n        }\n\n        if let Some(method) = c.scroll_method {\n            let _ = device.config_scroll_set_method(method.into());\n\n            if method == niri_config::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        } else if let Some(default) = device.config_scroll_default_method() {\n            let _ = device.config_scroll_set_method(default);\n\n            if default == input::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        }\n\n        if let Some(tap_button_map) = c.tap_button_map {\n            let _ = device.config_tap_set_button_map(tap_button_map.into());\n        } else if let Some(default) = device.config_tap_default_button_map() {\n            let _ = device.config_tap_set_button_map(default);\n        }\n\n        if let Some(method) = c.click_method {\n            let _ = device.config_click_set_method(method.into());\n        } else if let Some(default) = device.config_click_default_method() {\n            let _ = device.config_click_set_method(default);\n        }\n    }\n\n    // This is how Mutter tells apart mice.\n    let mut is_trackball = false;\n    let mut is_trackpoint = false;\n    if let Some(udev_device) = unsafe { device.udev_device() } {\n        if udev_device.property_value(\"ID_INPUT_TRACKBALL\").is_some() {\n            is_trackball = true;\n        }\n        if udev_device\n            .property_value(\"ID_INPUT_POINTINGSTICK\")\n            .is_some()\n        {\n            is_trackpoint = true;\n        }\n    }\n\n    let is_mouse = device.has_capability(input::DeviceCapability::Pointer)\n        && !is_touchpad\n        && !is_trackball\n        && !is_trackpoint;\n    if is_mouse {\n        let c = &config.mouse;\n        let _ = device.config_send_events_set_mode(if c.off {\n            input::SendEventsMode::DISABLED\n        } else {\n            input::SendEventsMode::ENABLED\n        });\n        let _ = device.config_scroll_set_natural_scroll_enabled(c.natural_scroll);\n        let _ = device.config_accel_set_speed(c.accel_speed.0);\n        let _ = device.config_left_handed_set(c.left_handed);\n        let _ = device.config_middle_emulation_set_enabled(c.middle_emulation);\n\n        if let Some(accel_profile) = c.accel_profile {\n            let _ = device.config_accel_set_profile(accel_profile.into());\n        } else if let Some(default) = device.config_accel_default_profile() {\n            let _ = device.config_accel_set_profile(default);\n        }\n\n        if let Some(method) = c.scroll_method {\n            let _ = device.config_scroll_set_method(method.into());\n\n            if method == niri_config::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        } else if let Some(default) = device.config_scroll_default_method() {\n            let _ = device.config_scroll_set_method(default);\n\n            if default == input::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        }\n    }\n\n    if is_trackball {\n        let c = &config.trackball;\n        let _ = device.config_send_events_set_mode(if c.off {\n            input::SendEventsMode::DISABLED\n        } else {\n            input::SendEventsMode::ENABLED\n        });\n        let _ = device.config_scroll_set_natural_scroll_enabled(c.natural_scroll);\n        let _ = device.config_accel_set_speed(c.accel_speed.0);\n        let _ = device.config_middle_emulation_set_enabled(c.middle_emulation);\n        let _ = device.config_left_handed_set(c.left_handed);\n\n        if let Some(accel_profile) = c.accel_profile {\n            let _ = device.config_accel_set_profile(accel_profile.into());\n        } else if let Some(default) = device.config_accel_default_profile() {\n            let _ = device.config_accel_set_profile(default);\n        }\n\n        if let Some(method) = c.scroll_method {\n            let _ = device.config_scroll_set_method(method.into());\n\n            if method == niri_config::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        } else if let Some(default) = device.config_scroll_default_method() {\n            let _ = device.config_scroll_set_method(default);\n\n            if default == input::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        }\n    }\n\n    if is_trackpoint {\n        let c = &config.trackpoint;\n        let _ = device.config_send_events_set_mode(if c.off {\n            input::SendEventsMode::DISABLED\n        } else {\n            input::SendEventsMode::ENABLED\n        });\n        let _ = device.config_scroll_set_natural_scroll_enabled(c.natural_scroll);\n        let _ = device.config_accel_set_speed(c.accel_speed.0);\n        let _ = device.config_left_handed_set(c.left_handed);\n        let _ = device.config_middle_emulation_set_enabled(c.middle_emulation);\n\n        if let Some(accel_profile) = c.accel_profile {\n            let _ = device.config_accel_set_profile(accel_profile.into());\n        } else if let Some(default) = device.config_accel_default_profile() {\n            let _ = device.config_accel_set_profile(default);\n        }\n\n        if let Some(method) = c.scroll_method {\n            let _ = device.config_scroll_set_method(method.into());\n\n            if method == niri_config::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        } else if let Some(default) = device.config_scroll_default_method() {\n            let _ = device.config_scroll_set_method(default);\n\n            if default == input::ScrollMethod::OnButtonDown {\n                if let Some(button) = c.scroll_button {\n                    let _ = device.config_scroll_set_button(button);\n                }\n                let _ = device.config_scroll_set_button_lock(if c.scroll_button_lock {\n                    input::ScrollButtonLockState::Enabled\n                } else {\n                    input::ScrollButtonLockState::Disabled\n                });\n            }\n        }\n    }\n\n    let is_tablet = device.has_capability(input::DeviceCapability::TabletTool);\n    if is_tablet {\n        let c = &config.tablet;\n        let _ = device.config_send_events_set_mode(if c.off {\n            input::SendEventsMode::DISABLED\n        } else {\n            input::SendEventsMode::ENABLED\n        });\n\n        #[rustfmt::skip]\n        const IDENTITY_MATRIX: [f32; 6] = [\n            1., 0., 0.,\n            0., 1., 0.,\n        ];\n\n        let _ = device.config_calibration_set_matrix(\n            c.calibration_matrix\n                .as_deref()\n                .and_then(|m| m.try_into().ok())\n                .or(device.config_calibration_default_matrix())\n                .unwrap_or(IDENTITY_MATRIX),\n        );\n\n        let _ = device.config_left_handed_set(c.left_handed);\n    }\n\n    let is_touch = device.has_capability(input::DeviceCapability::Touch);\n    if is_touch {\n        let c = &config.touch;\n        let _ = device.config_send_events_set_mode(if c.off {\n            input::SendEventsMode::DISABLED\n        } else {\n            input::SendEventsMode::ENABLED\n        });\n\n        #[rustfmt::skip]\n        const IDENTITY_MATRIX: [f32; 6] = [\n            1., 0., 0.,\n            0., 1., 0.,\n        ];\n\n        let _ = device.config_calibration_set_matrix(\n            c.calibration_matrix\n                .as_deref()\n                .and_then(|m| m.try_into().ok())\n                .or(device.config_calibration_default_matrix())\n                .unwrap_or(IDENTITY_MATRIX),\n        );\n    }\n}\n\npub fn mods_with_binds(mod_key: ModKey, binds: &Binds, triggers: &[Trigger]) -> HashSet<Modifiers> {\n    let mut rv = HashSet::new();\n    for bind in &binds.0 {\n        if !triggers.contains(&bind.key.trigger) {\n            continue;\n        }\n\n        let mut mods = bind.key.modifiers;\n        if mods.contains(Modifiers::COMPOSITOR) {\n            mods.remove(Modifiers::COMPOSITOR);\n            mods.insert(mod_key.to_modifiers());\n        }\n\n        rv.insert(mods);\n    }\n\n    rv\n}\n\npub fn mods_with_mouse_binds(mod_key: ModKey, binds: &Binds) -> HashSet<Modifiers> {\n    mods_with_binds(\n        mod_key,\n        binds,\n        &[\n            Trigger::MouseLeft,\n            Trigger::MouseRight,\n            Trigger::MouseMiddle,\n            Trigger::MouseBack,\n            Trigger::MouseForward,\n        ],\n    )\n}\n\npub fn mods_with_wheel_binds(mod_key: ModKey, binds: &Binds) -> HashSet<Modifiers> {\n    mods_with_binds(\n        mod_key,\n        binds,\n        &[\n            Trigger::WheelScrollUp,\n            Trigger::WheelScrollDown,\n            Trigger::WheelScrollLeft,\n            Trigger::WheelScrollRight,\n        ],\n    )\n}\n\npub fn mods_with_finger_scroll_binds(mod_key: ModKey, binds: &Binds) -> HashSet<Modifiers> {\n    mods_with_binds(\n        mod_key,\n        binds,\n        &[\n            Trigger::TouchpadScrollUp,\n            Trigger::TouchpadScrollDown,\n            Trigger::TouchpadScrollLeft,\n            Trigger::TouchpadScrollRight,\n        ],\n    )\n}\n\nfn grab_allows_hot_corner(grab: &(dyn PointerGrab<State> + 'static)) -> bool {\n    let grab = grab.as_any();\n\n    // We lean on the blocklist approach here since it's not a terribly big deal if hot corner\n    // works where it shouldn't, but it could prevent some workflows if the hot corner doesn't work\n    // when it should.\n    //\n    // Some notable grabs not mentioned here:\n    // - DnDGrab allows hot corner to DnD across workspaces.\n    // - ClickGrab keeps pointer focus on the window, so the hot corner doesn't trigger.\n    // - Touch grabs: touch doesn't trigger the hot corner.\n    if grab.is::<ResizeGrab>() || grab.is::<SpatialMovementGrab>() {\n        return false;\n    }\n\n    if let Some(grab) = grab.downcast_ref::<MoveGrab>() {\n        // Window move allows hot corner to DnD across workspaces.\n        if !grab.is_move() {\n            return false;\n        }\n    }\n\n    true\n}\n\n/// Returns an iterator over bindings.\n///\n/// Includes dynamically populated bindings like the MRU UI.\nfn make_binds_iter<'a>(\n    config: &'a Config,\n    mru: &'a mut WindowMruUi,\n    mods: Modifiers,\n) -> impl Iterator<Item = &'a Bind> + Clone {\n    // Figure out the binds to use depending on whether the MRU is enabled and/or open.\n    let general_binds = (!mru.is_open()).then_some(config.binds.0.iter());\n    let general_binds = general_binds.into_iter().flatten();\n\n    let mru_binds =\n        (config.recent_windows.on || mru.is_open()).then_some(config.recent_windows.binds.iter());\n    let mru_binds = mru_binds.into_iter().flatten();\n\n    let mru_open_binds = mru.is_open().then(|| mru.opened_bindings(mods));\n    let mru_open_binds = mru_open_binds.into_iter().flatten();\n\n    // General binds take precedence over the MRU binds.\n    general_binds.chain(mru_binds).chain(mru_open_binds)\n}\n\n#[cfg(test)]\nmod tests {\n    use std::cell::Cell;\n\n    use super::*;\n    use crate::animation::Clock;\n\n    #[test]\n    fn bindings_suppress_keys() {\n        let close_keysym = Keysym::q;\n        let bindings = Binds(vec![Bind {\n            key: Key {\n                trigger: Trigger::Keysym(close_keysym),\n                modifiers: Modifiers::COMPOSITOR | Modifiers::CTRL,\n            },\n            action: Action::CloseWindow,\n            repeat: true,\n            cooldown: None,\n            allow_when_locked: false,\n            allow_inhibiting: true,\n            hotkey_overlay_title: None,\n        }]);\n\n        let comp_mod = ModKey::Super;\n        let mut suppressed_keys = HashSet::new();\n\n        let screenshot_ui = ScreenshotUi::new(Clock::default(), Default::default());\n        let disable_power_key_handling = false;\n        let is_inhibiting_shortcuts = Cell::new(false);\n\n        // The key_code we pick is arbitrary, the only thing\n        // that matters is that they are different between cases.\n\n        let close_key_code = Keycode::from(close_keysym.raw() + 8u32);\n        let close_key_event = |suppr: &mut HashSet<Keycode>, mods: ModifiersState, pressed| {\n            should_intercept_key(\n                suppr,\n                &bindings.0,\n                comp_mod,\n                close_key_code,\n                close_keysym,\n                Some(close_keysym),\n                pressed,\n                mods,\n                &screenshot_ui,\n                disable_power_key_handling,\n                is_inhibiting_shortcuts.get(),\n            )\n        };\n\n        // Key event with the code which can't trigger any action.\n        let none_key_event = |suppr: &mut HashSet<Keycode>, mods: ModifiersState, pressed| {\n            should_intercept_key(\n                suppr,\n                &bindings.0,\n                comp_mod,\n                Keycode::from(Keysym::l.raw() + 8),\n                Keysym::l,\n                Some(Keysym::l),\n                pressed,\n                mods,\n                &screenshot_ui,\n                disable_power_key_handling,\n                is_inhibiting_shortcuts.get(),\n            )\n        };\n\n        let mut mods = ModifiersState {\n            logo: true,\n            ctrl: true,\n            ..Default::default()\n        };\n\n        // Action press/release.\n\n        let filter = close_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(\n            filter,\n            FilterResult::Intercept(Some(Bind {\n                action: Action::CloseWindow,\n                ..\n            }))\n        ));\n        assert!(suppressed_keys.contains(&close_key_code));\n\n        let filter = close_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Intercept(None)));\n        assert!(suppressed_keys.is_empty());\n\n        // Remove mod to make it for a binding.\n\n        mods.shift = true;\n        let filter = close_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(filter, FilterResult::Forward));\n\n        mods.shift = false;\n        let filter = close_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Forward));\n\n        // Just none press/release.\n\n        let filter = none_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(filter, FilterResult::Forward));\n\n        let filter = none_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Forward));\n\n        // Press action, press arbitrary, release action, release arbitrary.\n\n        let filter = close_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(\n            filter,\n            FilterResult::Intercept(Some(Bind {\n                action: Action::CloseWindow,\n                ..\n            }))\n        ));\n\n        let filter = none_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(filter, FilterResult::Forward));\n\n        let filter = close_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Intercept(None)));\n\n        let filter = none_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Forward));\n\n        // Trigger and remove all mods.\n\n        let filter = close_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(\n            filter,\n            FilterResult::Intercept(Some(Bind {\n                action: Action::CloseWindow,\n                ..\n            }))\n        ));\n\n        mods = Default::default();\n        let filter = close_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Intercept(None)));\n\n        // Ensure that no keys are being suppressed.\n        assert!(suppressed_keys.is_empty());\n\n        // Now test shortcut inhibiting.\n\n        // With inhibited shortcuts, we don't intercept our shortcut.\n        is_inhibiting_shortcuts.set(true);\n\n        mods = ModifiersState {\n            logo: true,\n            ctrl: true,\n            ..Default::default()\n        };\n\n        let filter = close_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(filter, FilterResult::Forward));\n        assert!(suppressed_keys.is_empty());\n\n        let filter = close_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Forward));\n        assert!(suppressed_keys.is_empty());\n\n        // Toggle it off after pressing the shortcut.\n        let filter = close_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(filter, FilterResult::Forward));\n        assert!(suppressed_keys.is_empty());\n\n        is_inhibiting_shortcuts.set(false);\n\n        let filter = close_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Forward));\n        assert!(suppressed_keys.is_empty());\n\n        // Toggle it on after pressing the shortcut.\n        let filter = close_key_event(&mut suppressed_keys, mods, true);\n        assert!(matches!(\n            filter,\n            FilterResult::Intercept(Some(Bind {\n                action: Action::CloseWindow,\n                ..\n            }))\n        ));\n        assert!(suppressed_keys.contains(&close_key_code));\n\n        is_inhibiting_shortcuts.set(true);\n\n        let filter = close_key_event(&mut suppressed_keys, mods, false);\n        assert!(matches!(filter, FilterResult::Intercept(None)));\n        assert!(suppressed_keys.is_empty());\n    }\n\n    #[test]\n    fn comp_mod_handling() {\n        let bindings = Binds(vec![\n            Bind {\n                key: Key {\n                    trigger: Trigger::Keysym(Keysym::q),\n                    modifiers: Modifiers::COMPOSITOR,\n                },\n                action: Action::CloseWindow,\n                repeat: true,\n                cooldown: None,\n                allow_when_locked: false,\n                allow_inhibiting: true,\n                hotkey_overlay_title: None,\n            },\n            Bind {\n                key: Key {\n                    trigger: Trigger::Keysym(Keysym::h),\n                    modifiers: Modifiers::SUPER,\n                },\n                action: Action::FocusColumnLeft,\n                repeat: true,\n                cooldown: None,\n                allow_when_locked: false,\n                allow_inhibiting: true,\n                hotkey_overlay_title: None,\n            },\n            Bind {\n                key: Key {\n                    trigger: Trigger::Keysym(Keysym::j),\n                    modifiers: Modifiers::empty(),\n                },\n                action: Action::FocusWindowDown,\n                repeat: true,\n                cooldown: None,\n                allow_when_locked: false,\n                allow_inhibiting: true,\n                hotkey_overlay_title: None,\n            },\n            Bind {\n                key: Key {\n                    trigger: Trigger::Keysym(Keysym::k),\n                    modifiers: Modifiers::COMPOSITOR | Modifiers::SUPER,\n                },\n                action: Action::FocusWindowUp,\n                repeat: true,\n                cooldown: None,\n                allow_when_locked: false,\n                allow_inhibiting: true,\n                hotkey_overlay_title: None,\n            },\n            Bind {\n                key: Key {\n                    trigger: Trigger::Keysym(Keysym::l),\n                    modifiers: Modifiers::SUPER | Modifiers::ALT,\n                },\n                action: Action::FocusColumnRight,\n                repeat: true,\n                cooldown: None,\n                allow_when_locked: false,\n                allow_inhibiting: true,\n                hotkey_overlay_title: None,\n            },\n        ]);\n\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::q),\n                ModifiersState {\n                    logo: true,\n                    ..Default::default()\n                }\n            )\n            .as_ref(),\n            Some(&bindings.0[0])\n        );\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::q),\n                ModifiersState::default(),\n            ),\n            None,\n        );\n\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::h),\n                ModifiersState {\n                    logo: true,\n                    ..Default::default()\n                }\n            )\n            .as_ref(),\n            Some(&bindings.0[1])\n        );\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::h),\n                ModifiersState::default(),\n            ),\n            None,\n        );\n\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::j),\n                ModifiersState {\n                    logo: true,\n                    ..Default::default()\n                }\n            ),\n            None,\n        );\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::j),\n                ModifiersState::default(),\n            )\n            .as_ref(),\n            Some(&bindings.0[2])\n        );\n\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::k),\n                ModifiersState {\n                    logo: true,\n                    ..Default::default()\n                }\n            )\n            .as_ref(),\n            Some(&bindings.0[3])\n        );\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::k),\n                ModifiersState::default(),\n            ),\n            None,\n        );\n\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::l),\n                ModifiersState {\n                    logo: true,\n                    alt: true,\n                    ..Default::default()\n                }\n            )\n            .as_ref(),\n            Some(&bindings.0[4])\n        );\n        assert_eq!(\n            find_configured_bind(\n                &bindings.0,\n                ModKey::Super,\n                Trigger::Keysym(Keysym::l),\n                ModifiersState {\n                    logo: true,\n                    ..Default::default()\n                },\n            ),\n            None,\n        );\n    }\n}\n"
  },
  {
    "path": "src/input/move_grab.rs",
    "content": "use std::time::Duration;\n\nuse smithay::backend::input::ButtonState;\nuse smithay::desktop::Window;\nuse smithay::input::pointer::{\n    AxisFrame, ButtonEvent, CursorIcon, CursorImageStatus, GestureHoldBeginEvent,\n    GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,\n    GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,\n    GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle,\n    RelativeMotionEvent,\n};\nuse smithay::input::touch::{\n    self, GrabStartData as TouchGrabStartData, TouchGrab, TouchInnerHandle,\n};\nuse smithay::input::SeatHandler;\nuse smithay::output::Output;\nuse smithay::utils::{IsAlive, Logical, Point, Serial, SERIAL_COUNTER};\n\nuse crate::input::PointerOrTouchStartData;\nuse crate::niri::State;\nuse crate::utils::get_monotonic_time;\n\npub struct MoveGrab {\n    start_data: PointerOrTouchStartData<State>,\n    start_output: Output,\n    start_pos_within_output: Point<f64, Logical>,\n    last_location: Point<f64, Logical>,\n    window: Window,\n    gesture: GestureState,\n    enable_view_offset: bool,\n    move_icon: CursorIcon,\n\n    // Accumulated and applied in frame().\n    new_location: Point<f64, Logical>,\n    event_timestamp: Option<Duration>,\n    relative_delta: Option<Point<f64, Logical>>,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum GestureState {\n    Recognizing,\n    Move,\n    ViewOffset,\n}\n\nimpl MoveGrab {\n    pub fn new(\n        state: &mut State,\n        start_data: PointerOrTouchStartData<State>,\n        window: Window,\n        enable_view_offset: bool,\n        move_icon: Option<CursorIcon>,\n    ) -> Option<Self> {\n        let location = start_data.location();\n        let (output, pos_within_output) = state.niri.output_under(location)?;\n\n        Some(Self {\n            last_location: location,\n            start_data,\n            start_output: output.clone(),\n            start_pos_within_output: pos_within_output,\n            window,\n            gesture: GestureState::Recognizing,\n            enable_view_offset,\n            // Moving windows by their titlebars uses the default cursor by default.\n            move_icon: move_icon.unwrap_or(CursorIcon::Default),\n            new_location: location,\n            event_timestamp: None,\n            relative_delta: None,\n        })\n    }\n\n    pub fn is_move(&self) -> bool {\n        self.gesture == GestureState::Move\n    }\n\n    pub fn view_offset_output(&self) -> Option<&Output> {\n        (self.gesture == GestureState::ViewOffset).then_some(&self.start_output)\n    }\n\n    fn on_ungrab(&mut self, data: &mut State) {\n        let layout = &mut data.niri.layout;\n        match self.gesture {\n            GestureState::Recognizing => {\n                // Activate the window on release. This is most prominent in the overview where\n                // windows are not activated on click. In the overview, we also try to do a nice\n                // synchronized workspace animation.\n                if layout.is_overview_open() {\n                    let res = layout.workspaces().find_map(|(mon, ws_idx, ws)| {\n                        ws.windows()\n                            .any(|w| w.window == self.window)\n                            .then(|| (mon.map(|mon| mon.output().clone()), ws_idx))\n                    });\n                    if let Some((Some(output), ws_idx)) = res {\n                        layout.focus_output(&output);\n                        layout.toggle_overview_to_workspace(ws_idx);\n                    }\n                }\n\n                layout.activate_window(&self.window);\n            }\n            GestureState::Move => layout.interactive_move_end(&self.window),\n            GestureState::ViewOffset => {\n                layout.view_offset_gesture_end(Some(false));\n            }\n        }\n\n        if self.start_data.is_pointer() {\n            data.niri\n                .cursor_manager\n                .set_cursor_image(CursorImageStatus::default_named());\n        }\n\n        // FIXME: only redraw the window output.\n        data.niri.queue_redraw_all();\n    }\n\n    fn begin_move(&mut self, data: &mut State) -> bool {\n        if !data.niri.layout.interactive_move_begin(\n            self.window.clone(),\n            &self.start_output,\n            self.start_pos_within_output,\n        ) {\n            // Can no longer start the move.\n            return false;\n        }\n\n        self.gesture = GestureState::Move;\n\n        if self.start_data.is_pointer() {\n            data.niri\n                .cursor_manager\n                .set_cursor_image(CursorImageStatus::Named(self.move_icon));\n        }\n\n        true\n    }\n\n    fn begin_view_offset(&mut self, data: &mut State) -> bool {\n        let layout = &mut data.niri.layout;\n        let Some(ws_idx) = layout.workspaces().find_map(|(mon, ws_idx, ws)| {\n            let ws_idx = ws\n                .windows()\n                .any(|w| w.window == self.window)\n                .then_some(ws_idx)?;\n            let output = mon?.output();\n\n            // If the window moved to a different output, don't start the gesture.\n            if *output != self.start_output {\n                return None;\n            }\n\n            Some(ws_idx)\n        }) else {\n            // Can no longer start the gesture.\n            return false;\n        };\n\n        layout.view_offset_gesture_begin(&self.start_output, Some(ws_idx), false);\n\n        self.gesture = GestureState::ViewOffset;\n\n        if self.start_data.is_pointer() {\n            data.niri\n                .cursor_manager\n                .set_cursor_image(CursorImageStatus::Named(CursorIcon::AllScroll));\n        }\n\n        true\n    }\n\n    fn on_frame(&mut self, data: &mut State) -> bool {\n        let Some(timestamp) = self.event_timestamp.take() else {\n            return true;\n        };\n\n        let mut delta = self.new_location - self.last_location;\n        let mut relative_delta = self.relative_delta.take().unwrap_or(delta);\n        self.last_location = self.new_location;\n\n        // Try to recognize the gesture.\n        if self.gesture == GestureState::Recognizing {\n            // Check if the window has closed.\n            if !self.window.alive() {\n                return false;\n            }\n\n            // Check if the gesture moved far enough to decide.\n            let c = self.new_location - self.start_data.location();\n            if c.x * c.x + c.y * c.y >= 8. * 8. {\n                let is_floating = data\n                    .niri\n                    .layout\n                    .workspaces()\n                    .find_map(|(_, _, ws)| {\n                        ws.windows()\n                            .any(|w| w.window == self.window)\n                            .then(|| ws.is_floating(&self.window))\n                    })\n                    .unwrap_or(false);\n\n                let is_view_offset =\n                    self.enable_view_offset && !is_floating && c.x.abs() > c.y.abs();\n\n                let started = if is_view_offset {\n                    self.begin_view_offset(data)\n                } else {\n                    self.begin_move(data)\n                };\n                if !started {\n                    return false;\n                }\n\n                // Apply the whole delta that accumulated during recognizing.\n                delta = c;\n                relative_delta = c;\n            }\n        }\n\n        match self.gesture {\n            GestureState::Recognizing => return true,\n            GestureState::Move => {\n                let Some((output, pos_within_output)) = data.niri.output_under(self.last_location)\n                else {\n                    return true;\n                };\n                let output = output.clone();\n\n                // Interactive move always uses absolute delta since the window must remain pinned\n                // to the cursor even when it's clamped to monitor bounds.\n                let ongoing = data.niri.layout.interactive_move_update(\n                    &self.window,\n                    delta,\n                    output,\n                    pos_within_output,\n                );\n                if ongoing {\n                    // FIXME: only redraw the previous and the new output.\n                    data.niri.queue_redraw_all();\n                    return true;\n                }\n            }\n            GestureState::ViewOffset => {\n                let res = data.niri.layout.view_offset_gesture_update(\n                    -relative_delta.x,\n                    timestamp,\n                    false,\n                );\n                if let Some(output) = res {\n                    if let Some(output) = output {\n                        data.niri.queue_redraw(&output);\n                    }\n                    return true;\n                }\n            }\n        }\n\n        false\n    }\n\n    fn on_toggle_floating(&mut self, data: &mut State) -> bool {\n        if self.gesture == GestureState::ViewOffset {\n            return true;\n        }\n\n        // Start move if still recognizing.\n        if self.gesture == GestureState::Recognizing {\n            let Some((output, pos_within_output)) = data.niri.output_under(self.last_location)\n            else {\n                return false;\n            };\n            let output = output.clone();\n\n            if !self.begin_move(data) {\n                return false;\n            }\n\n            // Apply the delta accumulated during recognizing.\n            let ongoing = data.niri.layout.interactive_move_update(\n                &self.window,\n                self.last_location - self.start_data.location(),\n                output,\n                pos_within_output,\n            );\n            if !ongoing {\n                return false;\n            }\n        }\n\n        data.niri.layout.toggle_window_floating(Some(&self.window));\n        data.niri.queue_redraw_all();\n\n        true\n    }\n}\n\nimpl PointerGrab<State> for MoveGrab {\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &MotionEvent,\n    ) {\n        // While the grab is active, no client has pointer focus.\n        handle.motion(data, None, event);\n\n        self.new_location = event.location;\n\n        // Relative motion takes precedence over normal motion.\n        if self.relative_delta.is_none() {\n            self.event_timestamp = Some(Duration::from_millis(u64::from(event.time)));\n        }\n    }\n\n    fn relative_motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &RelativeMotionEvent,\n    ) {\n        // While the grab is active, no client has pointer focus.\n        handle.relative_motion(data, None, event);\n\n        *self.relative_delta.get_or_insert_default() += event.delta;\n        self.event_timestamp = Some(Duration::from_micros(event.utime));\n    }\n\n    fn button(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &ButtonEvent,\n    ) {\n        handle.button(data, event);\n\n        let start_data = self.start_data.unwrap_pointer();\n\n        if !handle.current_pressed().contains(&start_data.button) {\n            // The button that initiated the grab was released.\n            handle.unset_grab(self, data, event.serial, event.time, true);\n            return;\n        }\n\n        // When moving with the left button, right toggles floating, and vice versa.\n        let toggle_floating_button = if start_data.button == 0x110 {\n            0x111\n        } else {\n            0x110\n        };\n        if event.state != ButtonState::Pressed || event.button != toggle_floating_button {\n            return;\n        }\n\n        if !self.on_toggle_floating(data) {\n            handle.unset_grab(self, data, event.serial, event.time, true);\n        }\n    }\n\n    fn axis(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        details: AxisFrame,\n    ) {\n        handle.axis(data, details);\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {\n        handle.frame(data);\n\n        if !self.on_frame(data) {\n            // The gesture is no longer ongoing.\n            handle.unset_grab(\n                self,\n                data,\n                SERIAL_COUNTER.next_serial(),\n                get_monotonic_time().as_millis() as u32,\n                true,\n            );\n        }\n    }\n\n    fn gesture_swipe_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeBeginEvent,\n    ) {\n        handle.gesture_swipe_begin(data, event);\n    }\n\n    fn gesture_swipe_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeUpdateEvent,\n    ) {\n        handle.gesture_swipe_update(data, event);\n    }\n\n    fn gesture_swipe_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeEndEvent,\n    ) {\n        handle.gesture_swipe_end(data, event);\n    }\n\n    fn gesture_pinch_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchBeginEvent,\n    ) {\n        handle.gesture_pinch_begin(data, event);\n    }\n\n    fn gesture_pinch_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchUpdateEvent,\n    ) {\n        handle.gesture_pinch_update(data, event);\n    }\n\n    fn gesture_pinch_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchEndEvent,\n    ) {\n        handle.gesture_pinch_end(data, event);\n    }\n\n    fn gesture_hold_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldBeginEvent,\n    ) {\n        handle.gesture_hold_begin(data, event);\n    }\n\n    fn gesture_hold_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldEndEvent,\n    ) {\n        handle.gesture_hold_end(data, event);\n    }\n\n    fn start_data(&self) -> &PointerGrabStartData<State> {\n        self.start_data.unwrap_pointer()\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n\nimpl TouchGrab<State> for MoveGrab {\n    fn down(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,\n        event: &touch::DownEvent,\n        seq: Serial,\n    ) {\n        handle.down(data, None, event, seq);\n\n        if event.slot == self.start_data.unwrap_touch().slot {\n            return;\n        }\n\n        if !self.on_toggle_floating(data) {\n            handle.unset_grab(self, data);\n        }\n    }\n\n    fn up(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &touch::UpEvent,\n        seq: Serial,\n    ) {\n        handle.up(data, event, seq);\n\n        if event.slot == self.start_data.unwrap_touch().slot {\n            handle.unset_grab(self, data);\n        }\n    }\n\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,\n        event: &touch::MotionEvent,\n        seq: Serial,\n    ) {\n        handle.motion(data, None, event, seq);\n\n        if event.slot != self.start_data.unwrap_touch().slot {\n            return;\n        }\n\n        self.new_location = event.location;\n        self.event_timestamp = Some(Duration::from_millis(u64::from(event.time)));\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {\n        handle.frame(data, seq);\n\n        if !self.on_frame(data) {\n            // The gesture is no longer ongoing.\n            handle.unset_grab(self, data);\n        }\n    }\n\n    fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {\n        handle.cancel(data, seq);\n        handle.unset_grab(self, data);\n    }\n\n    fn shape(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &touch::ShapeEvent,\n        seq: Serial,\n    ) {\n        handle.shape(data, event, seq);\n    }\n\n    fn orientation(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &touch::OrientationEvent,\n        seq: Serial,\n    ) {\n        handle.orientation(data, event, seq);\n    }\n\n    fn start_data(&self) -> &TouchGrabStartData<State> {\n        self.start_data.unwrap_touch()\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n"
  },
  {
    "path": "src/input/pick_color_grab.rs",
    "content": "use niri_ipc::PickedColor;\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::input::ButtonState;\nuse smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};\nuse smithay::backend::renderer::ExportMem as _;\nuse smithay::input::pointer::{\n    AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent,\n    GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,\n    GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,\n    MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,\n};\nuse smithay::input::SeatHandler;\nuse smithay::utils::{Logical, Physical, Point, Scale, Size, Transform};\n\nuse crate::niri::State;\nuse crate::render_helpers::{render_and_download, RenderTarget};\n\npub struct PickColorGrab {\n    start_data: PointerGrabStartData<State>,\n}\n\nimpl PickColorGrab {\n    pub fn new(start_data: PointerGrabStartData<State>) -> Self {\n        Self { start_data }\n    }\n\n    fn on_ungrab(&mut self, state: &mut State) {\n        if let Some(tx) = state.niri.pick_color.take() {\n            let _ = tx.send_blocking(None);\n        }\n        state\n            .niri\n            .cursor_manager\n            .set_cursor_image(CursorImageStatus::default_named());\n        state.niri.queue_redraw_all();\n    }\n\n    fn pick_color_at_point(location: Point<f64, Logical>, data: &mut State) -> Option<PickedColor> {\n        let (output, pos_within_output) = data.niri.output_under(location)?;\n        let output = output.clone();\n\n        data.backend\n            .with_primary_renderer(|renderer| {\n                data.niri.update_render_elements(Some(&output));\n\n                let scale = Scale::from(output.current_scale().fractional_scale());\n                // FIXME: perhaps replace floor with round once we figure out the pointer behavior\n                // at the bottom/right edges of the monitors.\n                let pos = pos_within_output.to_physical_precise_floor(scale);\n                let size = Size::<i32, Physical>::from((1, 1));\n\n                let elements = data.niri.render(\n                    renderer,\n                    &output,\n                    false,\n                    // This is an interactive operation so we can render without blocking out.\n                    RenderTarget::Output,\n                );\n\n                let mapping = match render_and_download(\n                    renderer,\n                    size,\n                    scale,\n                    Transform::Normal,\n                    Fourcc::Abgr8888,\n                    elements.iter().rev().map(|elem| {\n                        let offset = pos.upscale(-1);\n                        RelocateRenderElement::from_element(elem, offset, Relocate::Relative)\n                    }),\n                ) {\n                    Ok(mapping) => mapping,\n                    Err(_) => return None,\n                };\n                let pixels = match renderer.map_texture(&mapping) {\n                    Ok(pixels) => pixels,\n                    Err(_) => return None,\n                };\n\n                if pixels.len() == 4 {\n                    let rgb = [\n                        f64::from(pixels[0]) / 255.0,\n                        f64::from(pixels[1]) / 255.0,\n                        f64::from(pixels[2]) / 255.0,\n                    ];\n                    Some(PickedColor { rgb })\n                } else {\n                    error!(\n                        \"unexpected pixel data length: {} (expected 4)\",\n                        pixels.len()\n                    );\n                    None\n                }\n            })\n            .flatten()\n    }\n}\n\nimpl PointerGrab<State> for PickColorGrab {\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &MotionEvent,\n    ) {\n        handle.motion(data, None, event);\n    }\n\n    fn relative_motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &RelativeMotionEvent,\n    ) {\n        handle.relative_motion(data, None, event);\n    }\n\n    fn button(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &ButtonEvent,\n    ) {\n        if event.state != ButtonState::Pressed {\n            return;\n        }\n\n        // We're handling this press, don't send the release to the window.\n        data.niri.suppressed_buttons.insert(event.button);\n\n        if let Some(tx) = data.niri.pick_color.take() {\n            let color = Self::pick_color_at_point(handle.current_location(), data);\n            let _ = tx.send_blocking(color);\n        }\n\n        handle.unset_grab(self, data, event.serial, event.time, true);\n    }\n\n    fn axis(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        details: AxisFrame,\n    ) {\n        handle.axis(data, details);\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {\n        handle.frame(data);\n    }\n\n    fn gesture_swipe_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeBeginEvent,\n    ) {\n        handle.gesture_swipe_begin(data, event);\n    }\n\n    fn gesture_swipe_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeUpdateEvent,\n    ) {\n        handle.gesture_swipe_update(data, event);\n    }\n\n    fn gesture_swipe_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeEndEvent,\n    ) {\n        handle.gesture_swipe_end(data, event);\n    }\n\n    fn gesture_pinch_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchBeginEvent,\n    ) {\n        handle.gesture_pinch_begin(data, event);\n    }\n\n    fn gesture_pinch_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchUpdateEvent,\n    ) {\n        handle.gesture_pinch_update(data, event);\n    }\n\n    fn gesture_pinch_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchEndEvent,\n    ) {\n        handle.gesture_pinch_end(data, event);\n    }\n\n    fn gesture_hold_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldBeginEvent,\n    ) {\n        handle.gesture_hold_begin(data, event);\n    }\n\n    fn gesture_hold_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldEndEvent,\n    ) {\n        handle.gesture_hold_end(data, event);\n    }\n\n    fn start_data(&self) -> &PointerGrabStartData<State> {\n        &self.start_data\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n"
  },
  {
    "path": "src/input/pick_window_grab.rs",
    "content": "use smithay::backend::input::ButtonState;\nuse smithay::input::pointer::{\n    AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent,\n    GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,\n    GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,\n    MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,\n};\nuse smithay::input::SeatHandler;\nuse smithay::utils::{Logical, Point};\n\nuse crate::niri::State;\nuse crate::window::Mapped;\n\npub struct PickWindowGrab {\n    start_data: PointerGrabStartData<State>,\n}\n\nimpl PickWindowGrab {\n    pub fn new(start_data: PointerGrabStartData<State>) -> Self {\n        Self { start_data }\n    }\n\n    fn on_ungrab(&mut self, state: &mut State) {\n        if let Some(tx) = state.niri.pick_window.take() {\n            let _ = tx.send_blocking(None);\n        }\n        state\n            .niri\n            .cursor_manager\n            .set_cursor_image(CursorImageStatus::default_named());\n        // Redraw to update the cursor.\n        state.niri.queue_redraw_all();\n    }\n}\n\nimpl PointerGrab<State> for PickWindowGrab {\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &MotionEvent,\n    ) {\n        handle.motion(data, None, event);\n    }\n\n    fn relative_motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &RelativeMotionEvent,\n    ) {\n        handle.relative_motion(data, None, event);\n    }\n\n    fn button(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &ButtonEvent,\n    ) {\n        if event.state != ButtonState::Pressed {\n            return;\n        }\n\n        // We're handling this press, don't send the release to the window.\n        data.niri.suppressed_buttons.insert(event.button);\n\n        if let Some(tx) = data.niri.pick_window.take() {\n            let _ = tx.send_blocking(\n                data.niri\n                    .window_under(handle.current_location())\n                    .map(Mapped::id),\n            );\n        }\n\n        handle.unset_grab(self, data, event.serial, event.time, true);\n    }\n\n    fn axis(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        details: AxisFrame,\n    ) {\n        handle.axis(data, details);\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {\n        handle.frame(data);\n    }\n\n    fn gesture_swipe_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeBeginEvent,\n    ) {\n        handle.gesture_swipe_begin(data, event);\n    }\n\n    fn gesture_swipe_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeUpdateEvent,\n    ) {\n        handle.gesture_swipe_update(data, event);\n    }\n\n    fn gesture_swipe_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeEndEvent,\n    ) {\n        handle.gesture_swipe_end(data, event);\n    }\n\n    fn gesture_pinch_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchBeginEvent,\n    ) {\n        handle.gesture_pinch_begin(data, event);\n    }\n\n    fn gesture_pinch_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchUpdateEvent,\n    ) {\n        handle.gesture_pinch_update(data, event);\n    }\n\n    fn gesture_pinch_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchEndEvent,\n    ) {\n        handle.gesture_pinch_end(data, event);\n    }\n\n    fn gesture_hold_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldBeginEvent,\n    ) {\n        handle.gesture_hold_begin(data, event);\n    }\n\n    fn gesture_hold_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldEndEvent,\n    ) {\n        handle.gesture_hold_end(data, event);\n    }\n\n    fn start_data(&self) -> &PointerGrabStartData<State> {\n        &self.start_data\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n"
  },
  {
    "path": "src/input/resize_grab.rs",
    "content": "use smithay::desktop::Window;\nuse smithay::input::pointer::{\n    AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent,\n    GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,\n    GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,\n    MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,\n};\nuse smithay::input::SeatHandler;\nuse smithay::utils::{IsAlive, Logical, Point};\n\nuse crate::niri::State;\n\npub struct ResizeGrab {\n    start_data: PointerGrabStartData<State>,\n    window: Window,\n}\n\nimpl ResizeGrab {\n    pub fn new(start_data: PointerGrabStartData<State>, window: Window) -> Self {\n        Self { start_data, window }\n    }\n\n    fn on_ungrab(&mut self, state: &mut State) {\n        state.niri.layout.interactive_resize_end(&self.window);\n        state\n            .niri\n            .cursor_manager\n            .set_cursor_image(CursorImageStatus::default_named());\n    }\n}\n\nimpl PointerGrab<State> for ResizeGrab {\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &MotionEvent,\n    ) {\n        // While the grab is active, no client has pointer focus.\n        handle.motion(data, None, event);\n\n        if self.window.alive() {\n            let delta = event.location - self.start_data.location;\n            let ongoing = data\n                .niri\n                .layout\n                .interactive_resize_update(&self.window, delta);\n            if ongoing {\n                return;\n            }\n        }\n\n        // The resize is no longer ongoing.\n        handle.unset_grab(self, data, event.serial, event.time, true);\n    }\n\n    fn relative_motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &RelativeMotionEvent,\n    ) {\n        // While the grab is active, no client has pointer focus.\n        handle.relative_motion(data, None, event);\n    }\n\n    fn button(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &ButtonEvent,\n    ) {\n        handle.button(data, event);\n\n        if handle.current_pressed().is_empty() {\n            // No more buttons are pressed, release the grab.\n            handle.unset_grab(self, data, event.serial, event.time, true);\n        }\n    }\n\n    fn axis(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        details: AxisFrame,\n    ) {\n        handle.axis(data, details);\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {\n        handle.frame(data);\n    }\n\n    fn gesture_swipe_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeBeginEvent,\n    ) {\n        handle.gesture_swipe_begin(data, event);\n    }\n\n    fn gesture_swipe_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeUpdateEvent,\n    ) {\n        handle.gesture_swipe_update(data, event);\n    }\n\n    fn gesture_swipe_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeEndEvent,\n    ) {\n        handle.gesture_swipe_end(data, event);\n    }\n\n    fn gesture_pinch_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchBeginEvent,\n    ) {\n        handle.gesture_pinch_begin(data, event);\n    }\n\n    fn gesture_pinch_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchUpdateEvent,\n    ) {\n        handle.gesture_pinch_update(data, event);\n    }\n\n    fn gesture_pinch_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchEndEvent,\n    ) {\n        handle.gesture_pinch_end(data, event);\n    }\n\n    fn gesture_hold_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldBeginEvent,\n    ) {\n        handle.gesture_hold_begin(data, event);\n    }\n\n    fn gesture_hold_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldEndEvent,\n    ) {\n        handle.gesture_hold_end(data, event);\n    }\n\n    fn start_data(&self) -> &PointerGrabStartData<State> {\n        &self.start_data\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n"
  },
  {
    "path": "src/input/scroll_swipe_gesture.rs",
    "content": "//! Swipe gesture from scroll events.\n//!\n//! Tracks when to begin, update, and end a swipe gesture from pointer axis events, also whether\n//! the gesture is vertical or horizontal. Necessary because libinput only provides touchpad swipe\n//! gesture events for 3+ fingers.\n\n#[derive(Debug)]\npub struct ScrollSwipeGesture {\n    ongoing: bool,\n    vertical: bool,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Action {\n    BeginUpdate,\n    Update,\n    End,\n}\n\nimpl ScrollSwipeGesture {\n    pub const fn new() -> Self {\n        Self {\n            ongoing: false,\n            vertical: false,\n        }\n    }\n\n    pub fn update(&mut self, dx: f64, dy: f64) -> Action {\n        if dx == 0. && dy == 0. {\n            self.ongoing = false;\n            Action::End\n        } else if !self.ongoing {\n            self.ongoing = true;\n            self.vertical = dy != 0.;\n            Action::BeginUpdate\n        } else {\n            Action::Update\n        }\n    }\n\n    pub fn reset(&mut self) -> bool {\n        if self.ongoing {\n            self.ongoing = false;\n            true\n        } else {\n            false\n        }\n    }\n\n    pub fn is_vertical(&self) -> bool {\n        self.vertical\n    }\n}\n\nimpl Default for ScrollSwipeGesture {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Action {\n    pub fn begin(self) -> bool {\n        self == Action::BeginUpdate\n    }\n\n    pub fn end(self) -> bool {\n        self == Action::End\n    }\n}\n"
  },
  {
    "path": "src/input/scroll_tracker.rs",
    "content": "pub struct ScrollTracker {\n    tick: f64,\n    last: f64,\n    acc: f64,\n}\n\nimpl ScrollTracker {\n    #[allow(clippy::new_without_default)]\n    pub fn new(tick: i8) -> Self {\n        Self {\n            tick: f64::from(tick),\n            last: 0.,\n            acc: 0.,\n        }\n    }\n\n    pub fn accumulate(&mut self, amount: f64) -> i8 {\n        let changed_direction = (self.last > 0. && amount < 0.) || (self.last < 0. && amount > 0.);\n        if changed_direction {\n            self.acc = 0.\n        }\n\n        self.last = amount;\n        self.acc += amount;\n\n        let mut ticks = 0;\n        if self.acc.abs() >= self.tick {\n            let clamped = self.acc.clamp(-127. * self.tick, 127. * self.tick);\n            ticks = (clamped as i16 / self.tick as i16) as i8;\n            self.acc %= self.tick;\n        }\n\n        ticks\n    }\n\n    pub fn reset(&mut self) {\n        self.last = 0.;\n        self.acc = 0.;\n    }\n}\n"
  },
  {
    "path": "src/input/spatial_movement_grab.rs",
    "content": "use std::time::Duration;\n\nuse smithay::input::pointer::{\n    AxisFrame, ButtonEvent, CursorImageStatus, GestureHoldBeginEvent, GestureHoldEndEvent,\n    GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,\n    GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData,\n    MotionEvent, PointerGrab, PointerInnerHandle, RelativeMotionEvent,\n};\nuse smithay::input::SeatHandler;\nuse smithay::output::Output;\nuse smithay::utils::{Logical, Point, SERIAL_COUNTER};\n\nuse crate::layout::workspace::WorkspaceId;\nuse crate::niri::State;\nuse crate::utils::get_monotonic_time;\n\npub struct SpatialMovementGrab {\n    start_data: PointerGrabStartData<State>,\n    last_location: Point<f64, Logical>,\n    output: Output,\n    workspace_id: WorkspaceId,\n    gesture: GestureState,\n\n    // Accumulated and applied in frame().\n    new_location: Point<f64, Logical>,\n    event_timestamp: Option<Duration>,\n    relative_delta: Option<Point<f64, Logical>>,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum GestureState {\n    Recognizing,\n    ViewOffset,\n    WorkspaceSwitch,\n}\n\nimpl SpatialMovementGrab {\n    pub fn new(\n        start_data: PointerGrabStartData<State>,\n        output: Output,\n        workspace_id: WorkspaceId,\n        is_view_offset: bool,\n    ) -> Self {\n        let location = start_data.location;\n        let gesture = if is_view_offset {\n            GestureState::ViewOffset\n        } else {\n            GestureState::Recognizing\n        };\n\n        Self {\n            last_location: location,\n            start_data,\n            output,\n            workspace_id,\n            gesture,\n            new_location: location,\n            event_timestamp: None,\n            relative_delta: None,\n        }\n    }\n\n    pub fn view_offset_output(&self) -> Option<&Output> {\n        (self.gesture == GestureState::ViewOffset).then_some(&self.output)\n    }\n\n    pub fn workspace_switch_output(&self) -> Option<&Output> {\n        (self.gesture == GestureState::WorkspaceSwitch).then_some(&self.output)\n    }\n\n    fn on_frame(&mut self, data: &mut State) -> bool {\n        let Some(timestamp) = self.event_timestamp.take() else {\n            return true;\n        };\n\n        let delta = self\n            .relative_delta\n            .take()\n            .unwrap_or(self.new_location - self.last_location);\n        self.last_location = self.new_location;\n\n        let layout = &mut data.niri.layout;\n        let res = match self.gesture {\n            GestureState::Recognizing => {\n                let c = self.new_location - self.start_data.location;\n\n                // Check if the gesture moved far enough to decide. Threshold copied from GTK 4.\n                if c.x * c.x + c.y * c.y >= 8. * 8. {\n                    if c.x.abs() > c.y.abs() {\n                        self.gesture = GestureState::ViewOffset;\n                        if let Some((ws_idx, ws)) = layout.find_workspace_by_id(self.workspace_id) {\n                            if ws.current_output() == Some(&self.output) {\n                                layout.view_offset_gesture_begin(&self.output, Some(ws_idx), false);\n                                layout.view_offset_gesture_update(-c.x, timestamp, false)\n                            } else {\n                                None\n                            }\n                        } else {\n                            None\n                        }\n                    } else {\n                        self.gesture = GestureState::WorkspaceSwitch;\n                        layout.workspace_switch_gesture_begin(&self.output, false);\n                        layout.workspace_switch_gesture_update(-c.y, timestamp, false)\n                    }\n                } else {\n                    Some(None)\n                }\n            }\n            GestureState::ViewOffset => {\n                layout.view_offset_gesture_update(-delta.x, timestamp, false)\n            }\n            GestureState::WorkspaceSwitch => {\n                layout.workspace_switch_gesture_update(-delta.y, timestamp, false)\n            }\n        };\n\n        if let Some(output) = res {\n            if let Some(output) = output {\n                data.niri.queue_redraw(&output);\n            }\n            true\n        } else {\n            false\n        }\n    }\n\n    fn on_ungrab(&mut self, state: &mut State) {\n        let layout = &mut state.niri.layout;\n        let res = match self.gesture {\n            GestureState::Recognizing => None,\n            GestureState::ViewOffset => layout.view_offset_gesture_end(Some(false)),\n            GestureState::WorkspaceSwitch => layout.workspace_switch_gesture_end(Some(false)),\n        };\n\n        if let Some(output) = res {\n            state.niri.queue_redraw(&output);\n        }\n\n        state\n            .niri\n            .cursor_manager\n            .set_cursor_image(CursorImageStatus::default_named());\n    }\n}\n\nimpl PointerGrab<State> for SpatialMovementGrab {\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &MotionEvent,\n    ) {\n        // While the grab is active, no client has pointer focus.\n        handle.motion(data, None, event);\n\n        self.new_location = event.location;\n\n        // Relative motion takes precedence over normal motion.\n        if self.relative_delta.is_none() {\n            self.event_timestamp = Some(Duration::from_millis(u64::from(event.time)));\n        }\n    }\n\n    fn relative_motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::PointerFocus, Point<f64, Logical>)>,\n        event: &RelativeMotionEvent,\n    ) {\n        // While the grab is active, no client has pointer focus.\n        handle.relative_motion(data, None, event);\n\n        *self.relative_delta.get_or_insert_default() += event.delta;\n        self.event_timestamp = Some(Duration::from_micros(event.utime));\n    }\n\n    fn button(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &ButtonEvent,\n    ) {\n        handle.button(data, event);\n\n        if handle.current_pressed().is_empty() {\n            // No more buttons are pressed, release the grab.\n            handle.unset_grab(self, data, event.serial, event.time, true);\n        }\n    }\n\n    fn axis(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        details: AxisFrame,\n    ) {\n        handle.axis(data, details);\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {\n        handle.frame(data);\n\n        if !self.on_frame(data) {\n            // The gesture is no longer ongoing.\n            handle.unset_grab(\n                self,\n                data,\n                SERIAL_COUNTER.next_serial(),\n                get_monotonic_time().as_millis() as u32,\n                true,\n            );\n        }\n    }\n\n    fn gesture_swipe_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeBeginEvent,\n    ) {\n        handle.gesture_swipe_begin(data, event);\n    }\n\n    fn gesture_swipe_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeUpdateEvent,\n    ) {\n        handle.gesture_swipe_update(data, event);\n    }\n\n    fn gesture_swipe_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureSwipeEndEvent,\n    ) {\n        handle.gesture_swipe_end(data, event);\n    }\n\n    fn gesture_pinch_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchBeginEvent,\n    ) {\n        handle.gesture_pinch_begin(data, event);\n    }\n\n    fn gesture_pinch_update(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchUpdateEvent,\n    ) {\n        handle.gesture_pinch_update(data, event);\n    }\n\n    fn gesture_pinch_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GesturePinchEndEvent,\n    ) {\n        handle.gesture_pinch_end(data, event);\n    }\n\n    fn gesture_hold_begin(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldBeginEvent,\n    ) {\n        handle.gesture_hold_begin(data, event);\n    }\n\n    fn gesture_hold_end(\n        &mut self,\n        data: &mut State,\n        handle: &mut PointerInnerHandle<'_, State>,\n        event: &GestureHoldEndEvent,\n    ) {\n        handle.gesture_hold_end(data, event);\n    }\n\n    fn start_data(&self) -> &PointerGrabStartData<State> {\n        &self.start_data\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n"
  },
  {
    "path": "src/input/swipe_tracker.rs",
    "content": "use std::collections::VecDeque;\nuse std::time::Duration;\n\nconst HISTORY_LIMIT: Duration = Duration::from_millis(150);\nconst DECELERATION_TOUCHPAD: f64 = 0.997;\n\n#[derive(Debug)]\npub struct SwipeTracker {\n    history: VecDeque<Event>,\n    pos: f64,\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct Event {\n    delta: f64,\n    timestamp: Duration,\n}\n\nimpl SwipeTracker {\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        Self {\n            history: VecDeque::new(),\n            pos: 0.,\n        }\n    }\n\n    /// Pushes a new reading into the tracker.\n    pub fn push(&mut self, delta: f64, timestamp: Duration) {\n        // For the events that we care about, timestamps should always increase\n        // monotonically.\n        if let Some(last) = self.history.back() {\n            if timestamp < last.timestamp {\n                trace!(\n                    \"ignoring event with timestamp {timestamp:?} earlier than last {:?}\",\n                    last.timestamp\n                );\n                return;\n            }\n        }\n\n        self.history.push_back(Event { delta, timestamp });\n        self.pos += delta;\n\n        self.trim_history();\n    }\n\n    /// Returns the current gesture position.\n    pub fn pos(&self) -> f64 {\n        self.pos\n    }\n\n    /// Computes the current gesture velocity.\n    pub fn velocity(&self) -> f64 {\n        let (Some(first), Some(last)) = (self.history.front(), self.history.back()) else {\n            return 0.;\n        };\n\n        let total_time = (last.timestamp - first.timestamp).as_secs_f64();\n        if total_time == 0. {\n            return 0.;\n        }\n\n        let total_delta = self.history.iter().map(|event| event.delta).sum::<f64>();\n        total_delta / total_time\n    }\n\n    /// Computes the gesture end position after decelerating to a halt.\n    pub fn projected_end_pos(&self) -> f64 {\n        let vel = self.velocity();\n        self.pos - vel / (1000. * DECELERATION_TOUCHPAD.ln())\n    }\n\n    fn trim_history(&mut self) {\n        let Some(&Event { timestamp, .. }) = self.history.back() else {\n            return;\n        };\n\n        while let Some(first) = self.history.front() {\n            if timestamp <= first.timestamp + HISTORY_LIMIT {\n                break;\n            }\n\n            let _ = self.history.pop_front();\n        }\n    }\n}\n"
  },
  {
    "path": "src/input/touch_overview_grab.rs",
    "content": "use std::time::Duration;\n\nuse smithay::desktop::Window;\nuse smithay::input::touch::{\n    DownEvent, GrabStartData as TouchGrabStartData, MotionEvent, OrientationEvent, ShapeEvent,\n    TouchGrab, TouchInnerHandle, UpEvent,\n};\nuse smithay::input::SeatHandler;\nuse smithay::output::Output;\nuse smithay::utils::{IsAlive, Logical, Point, Serial};\n\nuse crate::layout::workspace::{Workspace, WorkspaceId};\nuse crate::niri::State;\nuse crate::window::Mapped;\n\n// When the touch is stationary for this much time, it becomes an interactive move.\nconst INTERACTIVE_MOVE_THRESHOLD: Duration = Duration::from_millis(500);\n\npub struct TouchOverviewGrab {\n    start_data: TouchGrabStartData<State>,\n    start_timestamp: Duration,\n    last_location: Point<f64, Logical>,\n    output: Output,\n    start_pos_within_output: Point<f64, Logical>,\n    workspace_id: Option<WorkspaceId>,\n    workspace_matched_narrow: bool,\n    window: Option<Window>,\n    gesture: GestureState,\n}\n\n#[derive(Debug, Clone, Copy)]\nenum GestureState {\n    Recognizing,\n    ViewOffset,\n    WorkspaceSwitch,\n    InteractiveMove,\n}\n\nimpl TouchOverviewGrab {\n    pub fn new(\n        start_data: TouchGrabStartData<State>,\n        start_timestamp: Duration,\n        output: Output,\n        start_pos_within_output: Point<f64, Logical>,\n        workspace_id: Option<WorkspaceId>,\n        workspace_matched_narrow: bool,\n        window: Option<Window>,\n    ) -> Self {\n        Self {\n            last_location: start_data.location,\n            start_timestamp,\n            start_data,\n            output,\n            start_pos_within_output,\n            workspace_id,\n            workspace_matched_narrow,\n            window,\n            gesture: GestureState::Recognizing,\n        }\n    }\n\n    fn on_ungrab(&mut self, state: &mut State) {\n        let layout = &mut state.niri.layout;\n        match self.gesture {\n            GestureState::Recognizing => {\n                // Tap to activate.\n                layout.focus_output(&self.output);\n\n                // Activate the workspace if necessary.\n                if self.window.is_some() || self.workspace_matched_narrow {\n                    // When activating a window, we want to activate the window's current\n                    // workspace. Otherwise, find the workspace that we tapped on.\n                    let ws_matches = |ws: &Workspace<Mapped>| {\n                        if let Some(window) = &self.window {\n                            ws.has_window(window)\n                        } else if let Some(ws_id) = self.workspace_id {\n                            ws.id() == ws_id\n                        } else {\n                            false\n                        }\n                    };\n\n                    let ws_idx = if let Some((Some(mon), ws_idx, _)) =\n                        layout.workspaces().find(|(_, _, ws)| ws_matches(ws))\n                    {\n                        // The workspace could've moved to a different output in the meantime.\n                        (*mon.output() == self.output).then_some(ws_idx)\n                    } else {\n                        None\n                    };\n\n                    if let Some(ws_idx) = ws_idx {\n                        layout.toggle_overview_to_workspace(ws_idx);\n                    }\n                }\n\n                if let Some(window) = self.window.as_ref() {\n                    layout.activate_window(window);\n                }\n            }\n            GestureState::ViewOffset => {\n                layout.view_offset_gesture_end(Some(false));\n            }\n            GestureState::WorkspaceSwitch => {\n                layout.workspace_switch_gesture_end(Some(false));\n            }\n            GestureState::InteractiveMove => {\n                layout.interactive_move_end(self.window.as_ref().unwrap());\n            }\n        };\n\n        state.niri.queue_redraw_all();\n    }\n}\n\nimpl TouchGrab<State> for TouchOverviewGrab {\n    fn down(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,\n        event: &DownEvent,\n        seq: Serial,\n    ) {\n        handle.down(data, None, event, seq);\n\n        if event.slot == self.start_data.slot {\n            return;\n        }\n\n        if matches!(self.gesture, GestureState::InteractiveMove) {\n            if let Some(window) = &self.window.as_ref() {\n                data.niri.layout.toggle_window_floating(Some(window));\n                data.niri.queue_redraw_all();\n            }\n        }\n    }\n\n    fn up(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &UpEvent,\n        seq: Serial,\n    ) {\n        handle.up(data, event, seq);\n\n        if event.slot != self.start_data.slot {\n            return;\n        }\n\n        handle.unset_grab(self, data);\n    }\n\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,\n        event: &MotionEvent,\n        seq: Serial,\n    ) {\n        handle.motion(data, None, event, seq);\n\n        if event.slot != self.start_data.slot {\n            return;\n        }\n\n        let timestamp = Duration::from_millis(u64::from(event.time));\n        let layout = &mut data.niri.layout;\n\n        // Check if we should become interactive move.\n        if matches!(self.gesture, GestureState::Recognizing) {\n            if let Some(window) = self.window.as_ref().filter(|win| win.alive()) {\n                let passed = timestamp.saturating_sub(self.start_timestamp);\n                if INTERACTIVE_MOVE_THRESHOLD <= passed\n                    && layout.interactive_move_begin(\n                        window.clone(),\n                        &self.output,\n                        self.start_pos_within_output,\n                    )\n                {\n                    self.gesture = GestureState::InteractiveMove;\n                }\n            }\n        }\n\n        // Check if we should become a spatial scroll.\n        if matches!(self.gesture, GestureState::Recognizing) {\n            let c = event.location - self.start_data.location;\n\n            // Check if the gesture moved far enough to decide. Threshold copied from libadwaita.\n            if c.x * c.x + c.y * c.y >= 16. * 16. {\n                if let Some(ws_id) = self.workspace_id.filter(|_| c.x.abs() > c.y.abs()) {\n                    if let Some((ws_idx, ws)) = layout.find_workspace_by_id(ws_id) {\n                        if ws.current_output() == Some(&self.output) {\n                            layout.view_offset_gesture_begin(&self.output, Some(ws_idx), false);\n                            self.gesture = GestureState::ViewOffset;\n                        }\n                    }\n                }\n\n                if matches!(self.gesture, GestureState::Recognizing) {\n                    layout.workspace_switch_gesture_begin(&self.output, false);\n                    self.gesture = GestureState::WorkspaceSwitch;\n                }\n            }\n        }\n\n        // Do nothing if still recognizing.\n        if matches!(self.gesture, GestureState::Recognizing) {\n            return;\n        }\n\n        let delta = event.location - self.last_location;\n        self.last_location = event.location;\n\n        let ongoing = match self.gesture {\n            GestureState::Recognizing => unreachable!(),\n            GestureState::ViewOffset => layout\n                .view_offset_gesture_update(-delta.x, timestamp, false)\n                .is_some(),\n            GestureState::WorkspaceSwitch => layout\n                .workspace_switch_gesture_update(-delta.y, timestamp, false)\n                .is_some(),\n            GestureState::InteractiveMove => {\n                let window = self.window.as_ref().unwrap();\n                if let Some((output, pos_within_output)) = data.niri.output_under(event.location) {\n                    let output = output.clone();\n                    data.niri.layout.interactive_move_update(\n                        window,\n                        delta,\n                        output,\n                        pos_within_output,\n                    )\n                } else {\n                    false\n                }\n            }\n        };\n\n        if ongoing {\n            data.niri.queue_redraw_all();\n        } else {\n            handle.unset_grab(self, data);\n        }\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {\n        handle.frame(data, seq);\n    }\n\n    fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {\n        handle.cancel(data, seq);\n        handle.unset_grab(self, data);\n    }\n\n    fn shape(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &ShapeEvent,\n        seq: Serial,\n    ) {\n        handle.shape(data, event, seq);\n    }\n\n    fn orientation(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &OrientationEvent,\n        seq: Serial,\n    ) {\n        handle.orientation(data, event, seq);\n    }\n\n    fn start_data(&self) -> &TouchGrabStartData<State> {\n        &self.start_data\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n"
  },
  {
    "path": "src/input/touch_resize_grab.rs",
    "content": "use smithay::desktop::Window;\nuse smithay::input::touch::{\n    DownEvent, GrabStartData as TouchGrabStartData, MotionEvent, OrientationEvent, ShapeEvent,\n    TouchGrab, TouchInnerHandle, UpEvent,\n};\nuse smithay::input::SeatHandler;\nuse smithay::utils::{IsAlive, Logical, Point, Serial};\n\nuse crate::niri::State;\n\npub struct TouchResizeGrab {\n    start_data: TouchGrabStartData<State>,\n    window: Window,\n}\n\nimpl TouchResizeGrab {\n    pub fn new(start_data: TouchGrabStartData<State>, window: Window) -> Self {\n        Self { start_data, window }\n    }\n\n    fn on_ungrab(&mut self, state: &mut State) {\n        state.niri.layout.interactive_resize_end(&self.window);\n    }\n}\n\nimpl TouchGrab<State> for TouchResizeGrab {\n    fn down(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,\n        event: &DownEvent,\n        seq: Serial,\n    ) {\n        handle.down(data, None, event, seq);\n    }\n\n    fn up(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &UpEvent,\n        seq: Serial,\n    ) {\n        handle.up(data, event, seq);\n\n        if event.slot != self.start_data.slot {\n            return;\n        }\n\n        handle.unset_grab(self, data);\n    }\n\n    fn motion(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        _focus: Option<(<State as SeatHandler>::TouchFocus, Point<f64, Logical>)>,\n        event: &MotionEvent,\n        seq: Serial,\n    ) {\n        handle.motion(data, None, event, seq);\n\n        if event.slot != self.start_data.slot {\n            return;\n        }\n\n        if self.window.alive() {\n            let delta = event.location - self.start_data.location;\n            let ongoing = data\n                .niri\n                .layout\n                .interactive_resize_update(&self.window, delta);\n            if ongoing {\n                return;\n            }\n        }\n\n        // The resize is no longer ongoing.\n        handle.unset_grab(self, data);\n    }\n\n    fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {\n        handle.frame(data, seq);\n    }\n\n    fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, seq: Serial) {\n        handle.cancel(data, seq);\n        handle.unset_grab(self, data);\n    }\n\n    fn shape(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &ShapeEvent,\n        seq: Serial,\n    ) {\n        handle.shape(data, event, seq);\n    }\n\n    fn orientation(\n        &mut self,\n        data: &mut State,\n        handle: &mut TouchInnerHandle<'_, State>,\n        event: &OrientationEvent,\n        seq: Serial,\n    ) {\n        handle.orientation(data, event, seq);\n    }\n\n    fn start_data(&self) -> &TouchGrabStartData<State> {\n        &self.start_data\n    }\n\n    fn unset(&mut self, data: &mut State) {\n        self.on_ungrab(data);\n    }\n}\n"
  },
  {
    "path": "src/ipc/client.rs",
    "content": "use std::io::ErrorKind;\nuse std::iter::Peekable;\nuse std::path::Path;\nuse std::{env, slice};\n\nuse anyhow::{anyhow, bail, Context};\nuse niri_config::OutputName;\nuse niri_ipc::socket::Socket;\nuse niri_ipc::{\n    Action, Cast, CastKind, CastTarget, Event, KeyboardLayouts, LogicalOutput, Mode, Output,\n    OutputConfigChanged, Overview, Request, Response, Transform, Window, WindowLayout,\n};\nuse serde_json::json;\n\nuse crate::cli::Msg;\nuse crate::utils::version;\n\npub fn handle_msg(mut msg: Msg, json: bool) -> anyhow::Result<()> {\n    // For actions taking paths, prepend the niri CLI's working directory.\n    if let Msg::Action {\n        action:\n            Action::Screenshot { path, .. }\n            | Action::ScreenshotScreen { path, .. }\n            | Action::ScreenshotWindow { path, .. },\n    } = &mut msg\n    {\n        if let Some(path) = path {\n            ensure_absolute_path(path).context(\"error making the path absolute\")?;\n        }\n    }\n\n    let request = match &msg {\n        Msg::Version => Request::Version,\n        Msg::Outputs => Request::Outputs,\n        Msg::FocusedWindow => Request::FocusedWindow,\n        Msg::FocusedOutput => Request::FocusedOutput,\n        Msg::PickWindow => Request::PickWindow,\n        Msg::PickColor => Request::PickColor,\n        Msg::Action { action } => Request::Action(action.clone()),\n        Msg::Output { output, action } => Request::Output {\n            output: output.clone(),\n            action: action.clone(),\n        },\n        Msg::Workspaces => Request::Workspaces,\n        Msg::Windows => Request::Windows,\n        Msg::Layers => Request::Layers,\n        Msg::KeyboardLayouts => Request::KeyboardLayouts,\n        Msg::EventStream => Request::EventStream,\n        Msg::RequestError => Request::ReturnError,\n        Msg::OverviewState => Request::OverviewState,\n        Msg::Casts => Request::Casts,\n    };\n\n    let mut socket = Socket::connect().context(\"error connecting to the niri socket\")?;\n\n    let result = socket.send(request);\n\n    // For errors that can be caused by a version mismatch between the running niri instance and\n    // the niri msg CLI, we will try to fetch and compare the versions.\n    let check_compositor_version = match &result {\n        Err(err) => {\n            // Response JSON parsing errors.\n            matches!(\n                err.kind(),\n                ErrorKind::InvalidData | ErrorKind::UnexpectedEof\n            )\n        }\n        // Error returned from niri.\n        Ok(Err(_)) => true,\n        _ => false,\n    };\n\n    let compositor_version = if check_compositor_version && !matches!(msg, Msg::Version) {\n        // Reconnect to support older niri versions with one request per connection.\n        Socket::connect()\n            .and_then(|mut socket| socket.send(Request::Version))\n            .ok()\n    } else {\n        None\n    };\n\n    // Default SIGPIPE so that our prints don't panic on stdout closing.\n    unsafe {\n        libc::signal(libc::SIGPIPE, libc::SIG_DFL);\n    }\n\n    // Check for CLI-server version mismatch to add helpful context.\n    match compositor_version {\n        Some(Ok(Response::Version(compositor_version))) => {\n            let cli_version = version();\n            if cli_version != compositor_version {\n                eprintln!(\"Running niri compositor has a different version from the niri CLI:\");\n                eprintln!(\"Compositor version: {compositor_version}\");\n                eprintln!(\"CLI version:        {cli_version}\");\n                eprintln!(\"Did you forget to restart niri after an update?\");\n                eprintln!();\n            }\n        }\n        Some(_) => {\n            eprintln!(\"Unable to get the running niri compositor version.\");\n            eprintln!(\"Did you forget to restart niri after an update?\");\n            eprintln!();\n        }\n        None => {\n            // Communication error, or the original request was already a version request, or the\n            // original request had succeeded. Don't add irrelevant context.\n        }\n    }\n\n    let reply = result.context(\"error communicating with niri\")?;\n    let response = reply.map_err(|err_msg| anyhow!(err_msg).context(\"niri returned an error\"))?;\n\n    match msg {\n        Msg::RequestError => {\n            bail!(\"unexpected response: expected an error, got {response:?}\");\n        }\n        Msg::Version => {\n            let Response::Version(compositor_version) = response else {\n                bail!(\"unexpected response: expected Version, got {response:?}\");\n            };\n\n            let cli_version = version();\n\n            if json {\n                println!(\n                    \"{}\",\n                    json!({\n                        \"compositor\": compositor_version,\n                        \"cli\": cli_version,\n                    })\n                );\n                return Ok(());\n            }\n\n            if cli_version != compositor_version {\n                eprintln!(\"Running niri compositor has a different version from the niri CLI.\");\n                eprintln!(\"Did you forget to restart niri after an update?\");\n                eprintln!();\n            }\n\n            println!(\"Compositor version: {compositor_version}\");\n            println!(\"CLI version:        {cli_version}\");\n        }\n        Msg::Outputs => {\n            let Response::Outputs(outputs) = response else {\n                bail!(\"unexpected response: expected Outputs, got {response:?}\");\n            };\n\n            if json {\n                let output =\n                    serde_json::to_string(&outputs).context(\"error formatting response\")?;\n                println!(\"{output}\");\n                return Ok(());\n            }\n\n            let mut outputs = outputs\n                .into_values()\n                .map(|out| (OutputName::from_ipc_output(&out), out))\n                .collect::<Vec<_>>();\n            outputs.sort_unstable_by(|a, b| a.0.compare(&b.0));\n\n            for (_name, output) in outputs.into_iter() {\n                print_output(output)?;\n                println!();\n            }\n        }\n        Msg::FocusedWindow => {\n            let Response::FocusedWindow(window) = response else {\n                bail!(\"unexpected response: expected FocusedWindow, got {response:?}\");\n            };\n\n            if json {\n                let window = serde_json::to_string(&window).context(\"error formatting response\")?;\n                println!(\"{window}\");\n                return Ok(());\n            }\n\n            if let Some(window) = window {\n                print_window(&window);\n            } else {\n                println!(\"No window is focused.\");\n            }\n        }\n        Msg::Windows => {\n            let Response::Windows(mut windows) = response else {\n                bail!(\"unexpected response: expected Windows, got {response:?}\");\n            };\n\n            if json {\n                let windows =\n                    serde_json::to_string(&windows).context(\"error formatting response\")?;\n                println!(\"{windows}\");\n                return Ok(());\n            }\n\n            windows.sort_unstable_by(|a, b| a.id.cmp(&b.id));\n\n            for window in windows {\n                print_window(&window);\n                println!();\n            }\n        }\n        Msg::Layers => {\n            let Response::Layers(mut layers) = response else {\n                bail!(\"unexpected response: expected Layers, got {response:?}\");\n            };\n\n            if json {\n                let layers = serde_json::to_string(&layers).context(\"error formatting response\")?;\n                println!(\"{layers}\");\n                return Ok(());\n            }\n\n            layers.sort_by(|a, b| {\n                Ord::cmp(&a.output, &b.output)\n                    .then_with(|| Ord::cmp(&a.layer, &b.layer))\n                    .then_with(|| Ord::cmp(&a.namespace, &b.namespace))\n            });\n            let mut iter = layers.iter().peekable();\n\n            let print = |surface: &niri_ipc::LayerSurface| {\n                println!(\"    Surface:\");\n                println!(\"      Namespace: \\\"{}\\\"\", &surface.namespace);\n\n                let interactivity = match surface.keyboard_interactivity {\n                    niri_ipc::LayerSurfaceKeyboardInteractivity::None => \"none\",\n                    niri_ipc::LayerSurfaceKeyboardInteractivity::Exclusive => \"exclusive\",\n                    niri_ipc::LayerSurfaceKeyboardInteractivity::OnDemand => \"on-demand\",\n                };\n                println!(\"      Keyboard interactivity: {interactivity}\");\n            };\n\n            let print_layer = |iter: &mut Peekable<slice::Iter<niri_ipc::LayerSurface>>,\n                               output: &str,\n                               layer| {\n                let mut empty = true;\n                while let Some(surface) = iter.next_if(|s| s.output == output && s.layer == layer) {\n                    empty = false;\n                    println!();\n                    print(surface);\n                }\n                if empty {\n                    println!(\" (empty)\\n\");\n                } else {\n                    println!();\n                }\n            };\n\n            while let Some(surface) = iter.peek() {\n                let output = &surface.output;\n                println!(\"Output \\\"{output}\\\":\");\n\n                print!(\"  Background layer:\");\n                print_layer(&mut iter, output, niri_ipc::Layer::Background);\n\n                print!(\"  Bottom layer:\");\n                print_layer(&mut iter, output, niri_ipc::Layer::Bottom);\n\n                print!(\"  Top layer:\");\n                print_layer(&mut iter, output, niri_ipc::Layer::Top);\n\n                print!(\"  Overlay layer:\");\n                print_layer(&mut iter, output, niri_ipc::Layer::Overlay);\n            }\n        }\n        Msg::FocusedOutput => {\n            let Response::FocusedOutput(output) = response else {\n                bail!(\"unexpected response: expected FocusedOutput, got {response:?}\");\n            };\n\n            if json {\n                let output = serde_json::to_string(&output).context(\"error formatting response\")?;\n                println!(\"{output}\");\n                return Ok(());\n            }\n\n            if let Some(output) = output {\n                print_output(output)?;\n            } else {\n                println!(\"No output is focused.\");\n            }\n        }\n        Msg::PickWindow => {\n            let Response::PickedWindow(window) = response else {\n                bail!(\"unexpected response: expected PickedWindow, got {response:?}\");\n            };\n\n            if json {\n                let window = serde_json::to_string(&window).context(\"error formatting response\")?;\n                println!(\"{window}\");\n                return Ok(());\n            }\n\n            if let Some(window) = window {\n                print_window(&window);\n            } else {\n                println!(\"No window selected.\");\n            }\n        }\n        Msg::PickColor => {\n            let Response::PickedColor(color) = response else {\n                bail!(\"unexpected response: expected PickedColor, got {response:?}\");\n            };\n\n            if json {\n                let color = serde_json::to_string(&color).context(\"error formatting response\")?;\n                println!(\"{color}\");\n                return Ok(());\n            }\n\n            if let Some(color) = color {\n                let [r, g, b] = color.rgb.map(|v| (v.clamp(0., 1.) * 255.).round() as u8);\n\n                println!(\"Picked color: rgb({r}, {g}, {b})\",);\n                println!(\"Hex: #{r:02x}{g:02x}{b:02x}\");\n            } else {\n                println!(\"No color was picked.\");\n            }\n        }\n        Msg::Action { .. } => {\n            let Response::Handled = response else {\n                bail!(\"unexpected response: expected Handled, got {response:?}\");\n            };\n        }\n        Msg::Output { output, .. } => {\n            let Response::OutputConfigChanged(response) = response else {\n                bail!(\"unexpected response: expected OutputConfigChanged, got {response:?}\");\n            };\n\n            if json {\n                let response =\n                    serde_json::to_string(&response).context(\"error formatting response\")?;\n                println!(\"{response}\");\n                return Ok(());\n            }\n\n            if response == OutputConfigChanged::OutputWasMissing {\n                println!(\"Output \\\"{output}\\\" is not connected.\");\n                println!(\"The change will apply when it is connected.\");\n            }\n        }\n        Msg::Workspaces => {\n            let Response::Workspaces(mut response) = response else {\n                bail!(\"unexpected response: expected Workspaces, got {response:?}\");\n            };\n\n            if json {\n                let response =\n                    serde_json::to_string(&response).context(\"error formatting response\")?;\n                println!(\"{response}\");\n                return Ok(());\n            }\n\n            if response.is_empty() {\n                println!(\"No workspaces.\");\n                return Ok(());\n            }\n\n            response.sort_by_key(|ws| ws.idx);\n            response.sort_by(|a, b| a.output.cmp(&b.output));\n\n            let mut current_output = if let Some(output) = response[0].output.as_deref() {\n                println!(\"Output \\\"{output}\\\":\");\n                Some(output)\n            } else {\n                println!(\"No output:\");\n                None\n            };\n\n            for ws in &response {\n                if ws.output.as_deref() != current_output {\n                    let output = ws.output.as_deref().context(\n                        \"invalid response: workspace with no output \\\n                         following a workspace with an output\",\n                    )?;\n                    current_output = Some(output);\n                    println!(\"\\nOutput \\\"{output}\\\":\");\n                }\n\n                let is_active = if ws.is_active { \" * \" } else { \"   \" };\n                let idx = ws.idx;\n                let name = if let Some(name) = ws.name.as_deref() {\n                    format!(\" \\\"{name}\\\"\")\n                } else {\n                    String::new()\n                };\n                println!(\"{is_active}{idx}{name}\");\n            }\n        }\n        Msg::KeyboardLayouts => {\n            let Response::KeyboardLayouts(response) = response else {\n                bail!(\"unexpected response: expected KeyboardLayouts, got {response:?}\");\n            };\n\n            if json {\n                let response =\n                    serde_json::to_string(&response).context(\"error formatting response\")?;\n                println!(\"{response}\");\n                return Ok(());\n            }\n\n            let KeyboardLayouts { names, current_idx } = response;\n            let current_idx = usize::from(current_idx);\n\n            println!(\"Keyboard layouts:\");\n            for (idx, name) in names.iter().enumerate() {\n                let is_active = if idx == current_idx { \" * \" } else { \"   \" };\n                println!(\"{is_active}{idx} {name}\");\n            }\n        }\n        Msg::EventStream => {\n            let Response::Handled = response else {\n                bail!(\"unexpected response: expected Handled, got {response:?}\");\n            };\n\n            if !json {\n                println!(\"Started reading events.\");\n            }\n\n            let mut read_event = socket.read_events();\n            loop {\n                let event = read_event().context(\"error reading event from niri\")?;\n\n                if json {\n                    let event = serde_json::to_string(&event).context(\"error formatting event\")?;\n                    println!(\"{event}\");\n                    continue;\n                }\n\n                match event {\n                    Event::WorkspacesChanged { workspaces } => {\n                        println!(\"Workspaces changed: {workspaces:?}\");\n                    }\n                    Event::WorkspaceUrgencyChanged { id, urgent } => {\n                        println!(\"Workspace {id}: urgency changed to {urgent}\");\n                    }\n                    Event::WorkspaceActivated { id, focused } => {\n                        let word = if focused { \"focused\" } else { \"activated\" };\n                        println!(\"Workspace {word}: {id}\");\n                    }\n                    Event::WorkspaceActiveWindowChanged {\n                        workspace_id,\n                        active_window_id,\n                    } => {\n                        println!(\n                            \"Workspace {workspace_id}: \\\n                             active window changed to {active_window_id:?}\"\n                        );\n                    }\n                    Event::WindowsChanged { windows } => {\n                        println!(\"Windows changed: {windows:?}\");\n                    }\n                    Event::WindowOpenedOrChanged { window } => {\n                        println!(\"Window opened or changed: {window:?}\");\n                    }\n                    Event::WindowClosed { id } => {\n                        println!(\"Window closed: {id}\");\n                    }\n                    Event::WindowFocusChanged { id } => {\n                        println!(\"Window focus changed: {id:?}\");\n                    }\n                    Event::WindowFocusTimestampChanged {\n                        id,\n                        focus_timestamp,\n                    } => {\n                        println!(\"Window {id}: focus timestamp changed to {focus_timestamp:?}\");\n                    }\n                    Event::WindowUrgencyChanged { id, urgent } => {\n                        println!(\"Window {id}: urgency changed to {urgent}\");\n                    }\n                    Event::WindowLayoutsChanged { changes } => {\n                        println!(\"Window layouts changed: {changes:?}\");\n                    }\n                    Event::KeyboardLayoutsChanged { keyboard_layouts } => {\n                        println!(\"Keyboard layouts changed: {keyboard_layouts:?}\");\n                    }\n                    Event::KeyboardLayoutSwitched { idx } => {\n                        println!(\"Keyboard layout switched: {idx}\");\n                    }\n                    Event::OverviewOpenedOrClosed { is_open: opened } => {\n                        println!(\"Overview toggled: {opened}\");\n                    }\n                    Event::ConfigLoaded { failed } => {\n                        let status = if failed {\n                            \"with an error\"\n                        } else {\n                            \"successfully\"\n                        };\n                        println!(\"Config loaded {status}\");\n                    }\n                    Event::ScreenshotCaptured { path } => {\n                        let mut parts = vec![];\n                        parts.push(\"copied to clipboard\".to_string());\n                        if let Some(path) = &path {\n                            parts.push(format!(\"saved to {path}\"));\n                        }\n                        let description = parts.join(\" and \");\n                        println!(\"Screenshot captured: {description}\");\n                    }\n                    Event::CastsChanged { casts } => {\n                        println!(\"Casts changed: {casts:?}\");\n                    }\n                    Event::CastStartedOrChanged { cast } => {\n                        println!(\"Cast started or changed: {cast:?}\");\n                    }\n                    Event::CastStopped { stream_id } => {\n                        println!(\"Cast stopped: stream id {stream_id}\");\n                    }\n                }\n            }\n        }\n        Msg::OverviewState => {\n            let Response::OverviewState(response) = response else {\n                bail!(\"unexpected response: expected Overview, got {response:?}\");\n            };\n\n            if json {\n                let response =\n                    serde_json::to_string(&response).context(\"error formatting response\")?;\n                println!(\"{response}\");\n                return Ok(());\n            }\n\n            let Overview { is_open } = response;\n            if is_open {\n                println!(\"Overview is open.\");\n            } else {\n                println!(\"Overview is closed.\");\n            }\n        }\n        Msg::Casts => {\n            let Response::Casts(mut casts) = response else {\n                bail!(\"unexpected response: expected Casts, got {response:?}\");\n            };\n\n            if json {\n                let casts = serde_json::to_string(&casts).context(\"error formatting response\")?;\n                println!(\"{casts}\");\n                return Ok(());\n            }\n\n            if casts.is_empty() {\n                println!(\"No screencasts.\");\n                return Ok(());\n            }\n\n            casts.sort_by_key(|c| (c.session_id, c.stream_id));\n            for cast in casts {\n                print_cast(&cast);\n                println!();\n            }\n        }\n    }\n\n    Ok(())\n}\n\nfn print_output(output: Output) -> anyhow::Result<()> {\n    let Output {\n        name,\n        make,\n        model,\n        serial,\n        physical_size,\n        modes,\n        current_mode,\n        is_custom_mode,\n        vrr_supported,\n        vrr_enabled,\n        logical,\n    } = output;\n\n    let serial = serial.as_deref().unwrap_or(\"Unknown\");\n    println!(r#\"Output \"{make} {model} {serial}\" ({name})\"#);\n\n    let print_qualifier = |is_preferred: bool, is_current: bool, is_custom_mode: bool| {\n        let mut qualifier = Vec::new();\n        if is_current {\n            qualifier.push(\"current\");\n            if is_custom_mode {\n                qualifier.push(\"custom\");\n            };\n        };\n\n        if is_preferred {\n            qualifier.push(\"preferred\");\n        };\n\n        if qualifier.is_empty() {\n            String::new()\n        } else {\n            format!(\" ({})\", qualifier.join(\", \"))\n        }\n    };\n\n    if let Some(current) = current_mode {\n        let mode = *modes\n            .get(current)\n            .context(\"invalid response: current mode does not exist\")?;\n        let Mode {\n            width,\n            height,\n            refresh_rate,\n            is_preferred,\n        } = mode;\n        let refresh = refresh_rate as f64 / 1000.;\n\n        // This is technically the current mode, but the println below already specifies that.\n        let qualifier = print_qualifier(is_preferred, false, is_custom_mode);\n        println!(\"  Current mode: {width}x{height} @ {refresh:.3} Hz{qualifier}\");\n    } else {\n        println!(\"  Disabled\");\n    }\n\n    if vrr_supported {\n        let enabled = if vrr_enabled { \"enabled\" } else { \"disabled\" };\n        println!(\"  Variable refresh rate: supported, {enabled}\");\n    } else {\n        println!(\"  Variable refresh rate: not supported\");\n    }\n\n    if let Some((width, height)) = physical_size {\n        println!(\"  Physical size: {width}x{height} mm\");\n    } else {\n        println!(\"  Physical size: unknown\");\n    }\n\n    if let Some(logical) = logical {\n        let LogicalOutput {\n            x,\n            y,\n            width,\n            height,\n            scale,\n            transform,\n        } = logical;\n        println!(\"  Logical position: {x}, {y}\");\n        println!(\"  Logical size: {width}x{height}\");\n        println!(\"  Scale: {scale}\");\n\n        let transform = match transform {\n            Transform::Normal => \"normal\",\n            Transform::_90 => \"90° counter-clockwise\",\n            Transform::_180 => \"180°\",\n            Transform::_270 => \"270° counter-clockwise\",\n            Transform::Flipped => \"flipped horizontally\",\n            Transform::Flipped90 => \"90° counter-clockwise, flipped horizontally\",\n            Transform::Flipped180 => \"flipped vertically\",\n            Transform::Flipped270 => \"270° counter-clockwise, flipped horizontally\",\n        };\n        println!(\"  Transform: {transform}\");\n    }\n\n    println!(\"  Available modes:\");\n    for (idx, mode) in modes.into_iter().enumerate() {\n        let Mode {\n            width,\n            height,\n            refresh_rate,\n            is_preferred,\n        } = mode;\n        let refresh = refresh_rate as f64 / 1000.;\n\n        let is_current = Some(idx) == current_mode;\n        let qualifier = print_qualifier(is_preferred, is_current, is_custom_mode);\n\n        println!(\"    {width}x{height}@{refresh:.3}{qualifier}\");\n    }\n    Ok(())\n}\n\nfn print_window(window: &Window) {\n    let focused = if window.is_focused { \" (focused)\" } else { \"\" };\n    let urgent = if window.is_urgent { \" (urgent)\" } else { \"\" };\n    println!(\"Window ID {}:{focused}{urgent}\", window.id);\n\n    if let Some(title) = &window.title {\n        println!(\"  Title: \\\"{title}\\\"\");\n    } else {\n        println!(\"  Title: (unset)\");\n    }\n\n    if let Some(app_id) = &window.app_id {\n        println!(\"  App ID: \\\"{app_id}\\\"\");\n    } else {\n        println!(\"  App ID: (unset)\");\n    }\n\n    println!(\n        \"  Is floating: {}\",\n        if window.is_floating { \"yes\" } else { \"no\" }\n    );\n\n    if let Some(pid) = window.pid {\n        println!(\"  PID: {pid}\");\n    } else {\n        println!(\"  PID: (unknown)\");\n    }\n\n    if let Some(workspace_id) = window.workspace_id {\n        println!(\"  Workspace ID: {workspace_id}\");\n    } else {\n        println!(\"  Workspace ID: (none)\");\n    }\n\n    let WindowLayout {\n        pos_in_scrolling_layout,\n        tile_size,\n        window_size,\n        tile_pos_in_workspace_view,\n        window_offset_in_tile,\n    } = window.layout;\n\n    println!(\"  Layout:\");\n    println!(\n        \"    Tile size: {} x {}\",\n        fmt_rounded(tile_size.0),\n        fmt_rounded(tile_size.1)\n    );\n\n    if let Some(pos) = pos_in_scrolling_layout {\n        println!(\"    Scrolling position: column {}, tile {}\", pos.0, pos.1);\n    }\n\n    if let Some(pos) = tile_pos_in_workspace_view {\n        println!(\n            \"    Workspace-view position: {}, {}\",\n            fmt_rounded(pos.0),\n            fmt_rounded(pos.1)\n        );\n    }\n\n    println!(\"    Window size: {} x {}\", window_size.0, window_size.1);\n    println!(\n        \"    Window offset in tile: {} x {}\",\n        fmt_rounded(window_offset_in_tile.0),\n        fmt_rounded(window_offset_in_tile.1)\n    );\n}\n\nfn print_cast(cast: &Cast) {\n    let active = if cast.is_active { \"\" } else { \" (inactive)\" };\n    println!(\"Cast stream ID {}:{active}\", cast.stream_id);\n    println!(\"  Session ID: {}\", cast.session_id);\n\n    let kind = match cast.kind {\n        CastKind::PipeWire => \"PipeWire\",\n        CastKind::WlrScreencopy => \"wlr-screencopy\",\n    };\n    println!(\"  Kind: {kind}\");\n\n    match &cast.target {\n        CastTarget::Nothing {} => {\n            println!(\"  Target: nothing (cleared)\");\n        }\n        CastTarget::Output { name } => {\n            println!(\"  Target: output \\\"{name}\\\"\");\n        }\n        CastTarget::Window { id } => {\n            println!(\"  Target: window {id}\");\n        }\n    }\n\n    if cast.is_dynamic_target {\n        println!(\"  Dynamic cast target\");\n    }\n\n    if let Some(pid) = cast.pid {\n        println!(\"  PID: {pid}\");\n    }\n\n    if let Some(node_id) = cast.pw_node_id {\n        println!(\"  PipeWire node ID: {node_id}\");\n    }\n}\n\nfn fmt_rounded(x: f64) -> String {\n    let r = x.round();\n    if (r - x).abs() <= 0.005 {\n        format!(\"{r}\")\n    } else {\n        format!(\"{x:.2}\")\n    }\n}\n\nfn ensure_absolute_path(path: &mut String) -> anyhow::Result<()> {\n    let p = Path::new(path);\n    if p.is_relative() {\n        let mut cwd = env::current_dir().context(\"error getting current working directory\")?;\n        cwd.push(p);\n        match cwd.into_os_string().into_string() {\n            Ok(absolute) => *path = absolute,\n            Err(cwd) => bail!(\"couldn't convert absolute path to string: {cwd:?}\"),\n        }\n    }\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::assert_snapshot;\n\n    use super::*;\n\n    #[test]\n    fn test_fmt_rounded() {\n        assert_snapshot!(fmt_rounded(1.9), @\"1.90\");\n        assert_snapshot!(fmt_rounded(1.994), @\"1.99\");\n        assert_snapshot!(fmt_rounded(1.996), @\"2\");\n        assert_snapshot!(fmt_rounded(2.0), @\"2\");\n        assert_snapshot!(fmt_rounded(2.004), @\"2\");\n        assert_snapshot!(fmt_rounded(2.006), @\"2.01\");\n        assert_snapshot!(fmt_rounded(2.1), @\"2.10\");\n    }\n}\n"
  },
  {
    "path": "src/ipc/mod.rs",
    "content": "pub mod client;\npub mod server;\n"
  },
  {
    "path": "src/ipc/server.rs",
    "content": "use std::cell::RefCell;\nuse std::collections::HashSet;\nuse std::ffi::OsStr;\nuse std::os::unix::net::{UnixListener, UnixStream};\nuse std::path::{Path, PathBuf};\nuse std::rc::Rc;\nuse std::sync::{Arc, Mutex};\nuse std::{env, io, process};\n\nuse anyhow::Context;\nuse async_channel::{Receiver, Sender, TrySendError};\nuse calloop::futures::Scheduler;\nuse calloop::io::Async;\nuse directories::BaseDirs;\nuse futures_util::io::{AsyncReadExt, BufReader};\nuse futures_util::{select_biased, AsyncBufReadExt, AsyncWrite, AsyncWriteExt, FutureExt as _};\nuse niri_config::OutputName;\nuse niri_ipc::state::{EventStreamState, EventStreamStatePart as _};\nuse niri_ipc::{\n    Action, Event, KeyboardLayouts, OutputConfigChanged, Overview, Reply, Request, Response,\n    Timestamp, WindowLayout, Workspace,\n};\nuse smithay::desktop::layer_map_for_output;\nuse smithay::input::pointer::{\n    CursorIcon, CursorImageStatus, Focus, GrabStartData as PointerGrabStartData,\n};\nuse smithay::reexports::calloop::generic::Generic;\nuse smithay::reexports::calloop::{Interest, LoopHandle, Mode, PostAction};\nuse smithay::reexports::rustix::fs::unlink;\nuse smithay::utils::SERIAL_COUNTER;\nuse smithay::wayland::shell::wlr_layer::{KeyboardInteractivity, Layer};\n\nuse crate::backend::IpcOutputMap;\nuse crate::input::pick_window_grab::PickWindowGrab;\nuse crate::layout::workspace::WorkspaceId;\nuse crate::niri::State;\nuse crate::utils::{version, with_toplevel_role};\nuse crate::window::Mapped;\n\n// If an event stream client fails to read events fast enough that we accumulate more than this\n// number in our buffer, we drop that event stream client.\nconst EVENT_STREAM_BUFFER_SIZE: usize = 64;\n\npub struct IpcServer {\n    /// Path to the IPC socket.\n    ///\n    /// This is `None` when creating `IpcServer` without a socket.\n    pub socket_path: Option<PathBuf>,\n    event_streams: Rc<RefCell<Vec<EventStreamSender>>>,\n    event_stream_state: Rc<RefCell<EventStreamState>>,\n}\n\nstruct ClientCtx {\n    event_loop: LoopHandle<'static, State>,\n    scheduler: Scheduler<()>,\n    ipc_outputs: Arc<Mutex<IpcOutputMap>>,\n    event_streams: Rc<RefCell<Vec<EventStreamSender>>>,\n    event_stream_state: Rc<RefCell<EventStreamState>>,\n}\n\nstruct EventStreamClient {\n    events: Receiver<Event>,\n    disconnect: Receiver<()>,\n    write: Box<dyn AsyncWrite + Unpin>,\n}\n\nstruct EventStreamSender {\n    events: Sender<Event>,\n    disconnect: Sender<()>,\n}\n\nimpl IpcServer {\n    pub fn start(\n        event_loop: &LoopHandle<'static, State>,\n        wayland_socket_name: Option<&OsStr>,\n    ) -> anyhow::Result<Self> {\n        let _span = tracy_client::span!(\"Ipc::start\");\n\n        let socket_path = if let Some(wayland_socket_name) = wayland_socket_name {\n            let wayland_socket_name = wayland_socket_name.to_string_lossy();\n            let socket_name = format!(\"niri.{wayland_socket_name}.{}.sock\", process::id());\n            let mut socket_path = socket_dir();\n            socket_path.push(socket_name);\n\n            let listener = UnixListener::bind(&socket_path).context(\"error binding socket\")?;\n            listener\n                .set_nonblocking(true)\n                .context(\"error setting socket to non-blocking\")?;\n\n            let source = Generic::new(listener, Interest::READ, Mode::Level);\n            event_loop\n                .insert_source(source, |_, socket, state| {\n                    match socket.accept() {\n                        Ok((stream, _)) => on_new_ipc_client(state, stream),\n                        Err(e) if e.kind() == io::ErrorKind::WouldBlock => (),\n                        Err(e) => return Err(e),\n                    }\n\n                    Ok(PostAction::Continue)\n                })\n                .unwrap();\n\n            Some(socket_path)\n        } else {\n            None\n        };\n\n        Ok(Self {\n            socket_path,\n            event_streams: Rc::new(RefCell::new(Vec::new())),\n            event_stream_state: Rc::new(RefCell::new(EventStreamState::default())),\n        })\n    }\n\n    fn send_event(&self, event: Event) {\n        let mut streams = self.event_streams.borrow_mut();\n        let mut to_remove = Vec::new();\n        for (idx, stream) in streams.iter_mut().enumerate() {\n            match stream.events.try_send(event.clone()) {\n                Ok(()) => (),\n                Err(TrySendError::Closed(_)) => to_remove.push(idx),\n                Err(TrySendError::Full(_)) => {\n                    warn!(\n                        \"disconnecting IPC event stream client \\\n                         because it is reading events too slowly\"\n                    );\n                    to_remove.push(idx);\n                }\n            }\n        }\n\n        for idx in to_remove.into_iter().rev() {\n            let stream = streams.swap_remove(idx);\n            let _ = stream.disconnect.send_blocking(());\n        }\n    }\n}\n\nimpl Drop for IpcServer {\n    fn drop(&mut self) {\n        if let Some(socket_path) = &self.socket_path {\n            let _ = unlink(socket_path);\n        }\n    }\n}\n\nfn socket_dir() -> PathBuf {\n    BaseDirs::new()\n        .as_ref()\n        .and_then(|x| x.runtime_dir())\n        .map(|x| x.to_owned())\n        .unwrap_or_else(env::temp_dir)\n}\n\nfn on_new_ipc_client(state: &mut State, stream: UnixStream) {\n    let _span = tracy_client::span!(\"on_new_ipc_client\");\n    trace!(\"new IPC client connected\");\n\n    let stream = match state.niri.event_loop.adapt_io(stream) {\n        Ok(stream) => stream,\n        Err(err) => {\n            warn!(\"error making IPC stream async: {err:?}\");\n            return;\n        }\n    };\n\n    let ipc_server = state.niri.ipc_server.as_ref().unwrap();\n\n    let ctx = ClientCtx {\n        event_loop: state.niri.event_loop.clone(),\n        scheduler: state.niri.scheduler.clone(),\n        ipc_outputs: state.backend.ipc_outputs(),\n        event_streams: ipc_server.event_streams.clone(),\n        event_stream_state: ipc_server.event_stream_state.clone(),\n    };\n\n    let future = async move {\n        if let Err(err) = handle_client(ctx, stream).await {\n            warn!(\"error handling IPC client: {err:?}\");\n        }\n    };\n    if let Err(err) = state.niri.scheduler.schedule(future) {\n        warn!(\"error scheduling IPC stream future: {err:?}\");\n    }\n}\n\nasync fn handle_client(ctx: ClientCtx, stream: Async<'static, UnixStream>) -> anyhow::Result<()> {\n    let (read, mut write) = stream.split();\n    let mut read = BufReader::new(read);\n\n    loop {\n        // Don't keep buf around to avoid clients wasting RAM by filling it with bogus data.\n        let mut buf = Vec::new();\n        let res = read.read_until(b'\\n', &mut buf).await;\n        match res {\n            Ok(0) => return Ok(()),\n            Ok(_) => (),\n            // Normal client disconnection.\n            Err(err) if err.kind() == io::ErrorKind::BrokenPipe => return Ok(()),\n            Err(err) => {\n                return Err(err).context(\"error reading request\");\n            }\n        }\n\n        let request = serde_json::from_slice(&buf)\n            .context(\"error parsing request\")\n            .map_err(|err| err.to_string());\n        let requested_error = matches!(request, Ok(Request::ReturnError));\n        let requested_event_stream = matches!(request, Ok(Request::EventStream));\n\n        let reply = match request {\n            Ok(request) => process(&ctx, request).await,\n            Err(err) => Err(err),\n        };\n\n        if let Err(err) = &reply {\n            if !requested_error {\n                warn!(\"error processing IPC request: {err:?}\");\n            }\n        }\n\n        buf.clear();\n        serde_json::to_writer(&mut buf, &reply).context(\"error formatting reply\")?;\n        buf.push(b'\\n');\n        write.write_all(&buf).await.context(\"error writing reply\")?;\n\n        if requested_event_stream {\n            let (events_tx, events_rx) = async_channel::bounded(EVENT_STREAM_BUFFER_SIZE);\n            let (disconnect_tx, disconnect_rx) = async_channel::bounded(1);\n\n            // Spawn a task for the client.\n            let client = EventStreamClient {\n                events: events_rx,\n                disconnect: disconnect_rx,\n                write: Box::new(write) as _,\n            };\n            let future = async move {\n                if let Err(err) = handle_event_stream_client(client).await {\n                    warn!(\"error handling IPC event stream client: {err:?}\");\n                }\n            };\n            if let Err(err) = ctx.scheduler.schedule(future) {\n                warn!(\"error scheduling IPC event stream future: {err:?}\");\n            }\n\n            // Send the initial state.\n            {\n                let state = ctx.event_stream_state.borrow();\n                for event in state.replicate() {\n                    events_tx\n                        .try_send(event)\n                        .expect(\"initial event burst had more events than buffer size\");\n                }\n            }\n\n            // Add it to the list.\n            {\n                let mut streams = ctx.event_streams.borrow_mut();\n                let sender = EventStreamSender {\n                    events: events_tx,\n                    disconnect: disconnect_tx,\n                };\n                streams.push(sender);\n            }\n\n            return Ok(());\n        }\n    }\n}\n\nasync fn process(ctx: &ClientCtx, request: Request) -> Reply {\n    let response = match request {\n        Request::ReturnError => return Err(String::from(\"example compositor error\")),\n        Request::Version => Response::Version(version()),\n        Request::Outputs => {\n            let ipc_outputs = ctx.ipc_outputs.lock().unwrap().clone();\n            let outputs = ipc_outputs.values().cloned().map(|o| (o.name.clone(), o));\n            Response::Outputs(outputs.collect())\n        }\n        Request::Workspaces => {\n            let state = ctx.event_stream_state.borrow();\n            let workspaces = state.workspaces.workspaces.values().cloned().collect();\n            Response::Workspaces(workspaces)\n        }\n        Request::Windows => {\n            let state = ctx.event_stream_state.borrow();\n            let windows = state.windows.windows.values().cloned().collect();\n            Response::Windows(windows)\n        }\n        Request::Layers => {\n            let (tx, rx) = async_channel::bounded(1);\n            ctx.event_loop.insert_idle(move |state| {\n                let mut layers = Vec::new();\n                for output in state.niri.global_space.outputs() {\n                    let name = output.name();\n                    for surface in layer_map_for_output(output).layers() {\n                        let layer = match surface.layer() {\n                            Layer::Background => niri_ipc::Layer::Background,\n                            Layer::Bottom => niri_ipc::Layer::Bottom,\n                            Layer::Top => niri_ipc::Layer::Top,\n                            Layer::Overlay => niri_ipc::Layer::Overlay,\n                        };\n                        let keyboard_interactivity =\n                            match surface.cached_state().keyboard_interactivity {\n                                KeyboardInteractivity::None => {\n                                    niri_ipc::LayerSurfaceKeyboardInteractivity::None\n                                }\n                                KeyboardInteractivity::Exclusive => {\n                                    niri_ipc::LayerSurfaceKeyboardInteractivity::Exclusive\n                                }\n                                KeyboardInteractivity::OnDemand => {\n                                    niri_ipc::LayerSurfaceKeyboardInteractivity::OnDemand\n                                }\n                            };\n\n                        layers.push(niri_ipc::LayerSurface {\n                            namespace: surface.namespace().to_owned(),\n                            output: name.clone(),\n                            layer,\n                            keyboard_interactivity,\n                        });\n                    }\n                }\n\n                let _ = tx.send_blocking(layers);\n            });\n            let result = rx.recv().await;\n            let layers = result.map_err(|_| String::from(\"error getting layers info\"))?;\n            Response::Layers(layers)\n        }\n        Request::KeyboardLayouts => {\n            let state = ctx.event_stream_state.borrow();\n            let layout = state.keyboard_layouts.keyboard_layouts.clone();\n            let layout = layout.expect(\"keyboard layouts should be set at startup\");\n            Response::KeyboardLayouts(layout)\n        }\n        Request::FocusedWindow => {\n            let state = ctx.event_stream_state.borrow();\n            let windows = &state.windows.windows;\n            let window = windows.values().find(|win| win.is_focused).cloned();\n            Response::FocusedWindow(window)\n        }\n        Request::PickWindow => {\n            let (tx, rx) = async_channel::bounded(1);\n            ctx.event_loop.insert_idle(move |state| {\n                let pointer = state.niri.seat.get_pointer().unwrap();\n                let start_data = PointerGrabStartData {\n                    focus: None,\n                    button: 0,\n                    location: pointer.current_location(),\n                };\n                let grab = PickWindowGrab::new(start_data);\n                // The `WindowPickGrab` ungrab handler will cancel the previous ongoing pick, if\n                // any.\n                pointer.set_grab(state, grab, SERIAL_COUNTER.next_serial(), Focus::Clear);\n                state.niri.pick_window = Some(tx);\n                state\n                    .niri\n                    .cursor_manager\n                    .set_cursor_image(CursorImageStatus::Named(CursorIcon::Crosshair));\n                // Redraw to update the cursor.\n                state.niri.queue_redraw_all();\n            });\n            let result = rx.recv().await;\n            let id = result.map_err(|_| String::from(\"error getting picked window info\"))?;\n            let window = id.and_then(|id| {\n                let state = ctx.event_stream_state.borrow();\n                state.windows.windows.get(&id.get()).cloned()\n            });\n            Response::PickedWindow(window)\n        }\n        Request::PickColor => {\n            let (tx, rx) = async_channel::bounded(1);\n            ctx.event_loop.insert_idle(move |state| {\n                state.handle_pick_color(tx);\n            });\n            let result = rx.recv().await;\n            let color = result.map_err(|_| String::from(\"error getting picked color\"))?;\n            Response::PickedColor(color)\n        }\n        Request::Action(action) => {\n            validate_action(&action)?;\n\n            let (tx, rx) = async_channel::bounded(1);\n\n            let action = niri_config::Action::from(action);\n            ctx.event_loop.insert_idle(move |state| {\n                // Make sure some logic like workspace clean-up has a chance to run before doing\n                // actions.\n                state.niri.advance_animations();\n                state.do_action(action, false);\n                let _ = tx.send_blocking(());\n            });\n\n            // Wait until the action has been processed before returning. This is important for a\n            // few actions, for instance for DoScreenTransition this wait ensures that the screen\n            // contents were sampled into the texture.\n            let _ = rx.recv().await;\n            Response::Handled\n        }\n        Request::Output { output, action } => {\n            action.validate()?;\n\n            let ipc_outputs = ctx.ipc_outputs.lock().unwrap();\n            let found = ipc_outputs\n                .values()\n                .any(|o| OutputName::from_ipc_output(o).matches(&output));\n            let response = if found {\n                OutputConfigChanged::Applied\n            } else {\n                OutputConfigChanged::OutputWasMissing\n            };\n            drop(ipc_outputs);\n\n            ctx.event_loop.insert_idle(move |state| {\n                state.apply_transient_output_config(&output, action);\n            });\n\n            Response::OutputConfigChanged(response)\n        }\n        Request::FocusedOutput => {\n            let (tx, rx) = async_channel::bounded(1);\n            ctx.event_loop.insert_idle(move |state| {\n                let active_output = state\n                    .niri\n                    .layout\n                    .active_output()\n                    .map(|output| output.name());\n\n                let output = active_output.and_then(|active_output| {\n                    state\n                        .backend\n                        .ipc_outputs()\n                        .lock()\n                        .unwrap()\n                        .values()\n                        .find(|o| o.name == active_output)\n                        .cloned()\n                });\n\n                let _ = tx.send_blocking(output);\n            });\n            let result = rx.recv().await;\n            let output = result.map_err(|_| String::from(\"error getting active output info\"))?;\n            Response::FocusedOutput(output)\n        }\n        Request::EventStream => Response::Handled,\n        Request::OverviewState => {\n            let state = ctx.event_stream_state.borrow();\n            let is_open = state.overview.is_open;\n            Response::OverviewState(Overview { is_open })\n        }\n        Request::Casts => {\n            let state = ctx.event_stream_state.borrow();\n            let casts = state.casts.casts.values().cloned().collect();\n            Response::Casts(casts)\n        }\n    };\n\n    Ok(response)\n}\n\nfn validate_action(action: &Action) -> Result<(), String> {\n    if let Action::Screenshot { path, .. }\n    | Action::ScreenshotScreen { path, .. }\n    | Action::ScreenshotWindow { path, .. }\n    | Action::LoadConfigFile { path } = action\n    {\n        if let Some(path) = path {\n            // Relative paths are resolved against the niri compositor's working directory, which\n            // is almost certainly not what you want.\n            if !Path::new(path).is_absolute() {\n                return Err(format!(\"path must be absolute: {path}\"));\n            }\n        }\n    }\n\n    if let Action::LoadConfigFile { path: Some(path) } = action {\n        let p = Path::new(path);\n        if !p.is_file() {\n            return Err(format!(\"path does not point to a file: {path}\"));\n        }\n    }\n\n    Ok(())\n}\n\nasync fn handle_event_stream_client(client: EventStreamClient) -> anyhow::Result<()> {\n    let EventStreamClient {\n        events,\n        disconnect,\n        mut write,\n    } = client;\n\n    while let Ok(event) = events.recv().await {\n        let mut buf = serde_json::to_vec(&event).context(\"error formatting event\")?;\n        buf.push(b'\\n');\n\n        let res = select_biased! {\n            _ = disconnect.recv().fuse() => return Ok(()),\n            res = write.write_all(&buf).fuse() => res,\n        };\n\n        match res {\n            Ok(()) => (),\n            // Normal client disconnection.\n            Err(err) if err.kind() == io::ErrorKind::BrokenPipe => return Ok(()),\n            res @ Err(_) => res.context(\"error writing event\")?,\n        }\n    }\n\n    Ok(())\n}\n\nfn make_ipc_window(\n    mapped: &Mapped,\n    workspace_id: Option<WorkspaceId>,\n    layout: WindowLayout,\n) -> niri_ipc::Window {\n    with_toplevel_role(mapped.toplevel(), |role| niri_ipc::Window {\n        id: mapped.id().get(),\n        title: role.title.clone(),\n        app_id: role.app_id.clone(),\n        pid: mapped.credentials().map(|c| c.pid),\n        workspace_id: workspace_id.map(|id| id.get()),\n        is_focused: mapped.is_focused(),\n        is_floating: mapped.is_floating(),\n        is_urgent: mapped.is_urgent(),\n        layout,\n        focus_timestamp: mapped.get_focus_timestamp().map(Timestamp::from),\n    })\n}\n\nimpl State {\n    pub fn ipc_keyboard_layouts_changed(&mut self) {\n        let keyboard = self.niri.seat.get_keyboard().unwrap();\n        let keyboard_layouts = keyboard.with_xkb_state(self, |context| {\n            let xkb = context.xkb().lock().unwrap();\n            let layouts = xkb.layouts();\n            KeyboardLayouts {\n                names: layouts\n                    .map(|layout| xkb.layout_name(layout).to_owned())\n                    .collect(),\n                current_idx: xkb.active_layout().0 as u8,\n            }\n        });\n\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n\n        let mut state = server.event_stream_state.borrow_mut();\n        let state = &mut state.keyboard_layouts;\n\n        let event = Event::KeyboardLayoutsChanged { keyboard_layouts };\n        state.apply(event.clone());\n        server.send_event(event);\n    }\n\n    pub fn ipc_refresh_keyboard_layout_index(&mut self) {\n        let keyboard = self.niri.seat.get_keyboard().unwrap();\n        let idx = keyboard.with_xkb_state(self, |context| {\n            let xkb = context.xkb().lock().unwrap();\n            xkb.active_layout().0 as u8\n        });\n\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n\n        let mut state = server.event_stream_state.borrow_mut();\n        let state = &mut state.keyboard_layouts;\n\n        if state.keyboard_layouts.as_ref().unwrap().current_idx == idx {\n            return;\n        }\n\n        let event = Event::KeyboardLayoutSwitched { idx };\n        state.apply(event.clone());\n        server.send_event(event);\n    }\n\n    pub fn ipc_refresh_layout(&mut self) {\n        self.ipc_refresh_workspaces();\n        self.ipc_refresh_windows();\n        self.ipc_refresh_overview();\n    }\n\n    fn ipc_refresh_workspaces(&mut self) {\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n\n        let _span = tracy_client::span!(\"State::ipc_refresh_workspaces\");\n\n        let mut state = server.event_stream_state.borrow_mut();\n        let state = &mut state.workspaces;\n\n        let mut events = Vec::new();\n        let layout = &self.niri.layout;\n        let focused_ws_id = layout.active_workspace().map(|ws| ws.id().get());\n\n        // Check for workspace changes.\n        let mut seen = HashSet::new();\n        let mut need_workspaces_changed = false;\n        for (mon, ws_idx, ws) in layout.workspaces() {\n            let id = ws.id().get();\n            seen.insert(id);\n\n            let Some(ipc_ws) = state.workspaces.get(&id) else {\n                // A new workspace was added.\n                need_workspaces_changed = true;\n                break;\n            };\n\n            // Check for any changes that we can't signal as individual events.\n            let output_name = mon.map(|mon| mon.output_name());\n            if ipc_ws.idx != u8::try_from(ws_idx + 1).unwrap_or(u8::MAX)\n                || ipc_ws.name.as_ref() != ws.name()\n                || ipc_ws.output.as_ref() != output_name\n            {\n                need_workspaces_changed = true;\n                break;\n            }\n\n            let active_window_id = ws.active_window().map(|win| win.id().get());\n            if ipc_ws.active_window_id != active_window_id {\n                events.push(Event::WorkspaceActiveWindowChanged {\n                    workspace_id: id,\n                    active_window_id,\n                });\n            }\n\n            // Check if this workspace urgent state changed.\n            let urgent = ws.is_urgent();\n            if urgent != ipc_ws.is_urgent {\n                events.push(Event::WorkspaceUrgencyChanged { id, urgent });\n            }\n\n            // Check if this workspace became focused.\n            let is_focused = Some(id) == focused_ws_id;\n            if is_focused && !ipc_ws.is_focused {\n                events.push(Event::WorkspaceActivated { id, focused: true });\n                continue;\n            }\n\n            // Check if this workspace became active.\n            let is_active = mon.is_some_and(|mon| mon.active_workspace_idx() == ws_idx);\n            if is_active && !ipc_ws.is_active {\n                events.push(Event::WorkspaceActivated { id, focused: false });\n            }\n        }\n\n        // Check if any workspaces were removed.\n        if !need_workspaces_changed && state.workspaces.keys().any(|id| !seen.contains(id)) {\n            need_workspaces_changed = true;\n        }\n\n        if need_workspaces_changed {\n            events.clear();\n\n            let workspaces = layout\n                .workspaces()\n                .map(|(mon, ws_idx, ws)| {\n                    let id = ws.id().get();\n                    Workspace {\n                        id,\n                        idx: u8::try_from(ws_idx + 1).unwrap_or(u8::MAX),\n                        name: ws.name().cloned(),\n                        output: mon.map(|mon| mon.output_name().clone()),\n                        is_urgent: ws.is_urgent(),\n                        is_active: mon.is_some_and(|mon| mon.active_workspace_idx() == ws_idx),\n                        is_focused: Some(id) == focused_ws_id,\n                        active_window_id: ws.active_window().map(|win| win.id().get()),\n                    }\n                })\n                .collect();\n\n            events.push(Event::WorkspacesChanged { workspaces });\n        }\n\n        for event in events {\n            state.apply(event.clone());\n            server.send_event(event);\n        }\n    }\n\n    fn ipc_refresh_windows(&mut self) {\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n\n        let _span = tracy_client::span!(\"State::ipc_refresh_windows\");\n\n        let mut state = server.event_stream_state.borrow_mut();\n        let state = &mut state.windows;\n\n        let mut events = Vec::new();\n        let layout = &self.niri.layout;\n\n        let mut batch_change_layouts: Vec<(u64, WindowLayout)> = Vec::new();\n\n        // Check for window changes.\n        let mut seen = HashSet::new();\n        let mut focused_id = None;\n        layout.with_windows(|mapped, _, ws_id, window_layout| {\n            let id = mapped.id().get();\n            seen.insert(id);\n\n            if mapped.is_focused() {\n                focused_id = Some(id);\n            }\n\n            let Some(ipc_win) = state.windows.get(&id) else {\n                let window = make_ipc_window(mapped, ws_id, window_layout);\n                events.push(Event::WindowOpenedOrChanged { window });\n                return;\n            };\n\n            let workspace_id = ws_id.map(|id| id.get());\n            let mut changed =\n                ipc_win.workspace_id != workspace_id || ipc_win.is_floating != mapped.is_floating();\n\n            changed |= with_toplevel_role(mapped.toplevel(), |role| {\n                ipc_win.title != role.title || ipc_win.app_id != role.app_id\n            });\n\n            if changed {\n                let window = make_ipc_window(mapped, ws_id, window_layout);\n                events.push(Event::WindowOpenedOrChanged { window });\n                return;\n            }\n\n            if ipc_win.layout != window_layout {\n                batch_change_layouts.push((id, window_layout));\n            }\n\n            if mapped.is_focused() && !ipc_win.is_focused {\n                events.push(Event::WindowFocusChanged { id: Some(id) });\n            }\n\n            let focus_timestamp = mapped.get_focus_timestamp().map(Timestamp::from);\n            if focus_timestamp != ipc_win.focus_timestamp {\n                events.push(Event::WindowFocusTimestampChanged {\n                    id,\n                    focus_timestamp,\n                });\n            }\n\n            let urgent = mapped.is_urgent();\n            if urgent != ipc_win.is_urgent {\n                events.push(Event::WindowUrgencyChanged { id, urgent })\n            }\n        });\n\n        // It might make sense to push layout changes after closed windows (since windows about to\n        // be closed will occupy the same column/tile positions as the window that moved into this\n        // vacated space), but also we are already pushing some layout changes in\n        // WindowOpenedOrChanged above, meaning that the receiving end has to handle this case\n        // anyway.\n        if !batch_change_layouts.is_empty() {\n            events.push(Event::WindowLayoutsChanged {\n                changes: batch_change_layouts,\n            });\n        }\n\n        // Check for closed windows.\n        let mut ipc_focused_id = None;\n        for (id, ipc_win) in &state.windows {\n            if !seen.contains(id) {\n                events.push(Event::WindowClosed { id: *id });\n            }\n\n            if ipc_win.is_focused {\n                ipc_focused_id = Some(id);\n            }\n        }\n\n        // Extra check for focus becoming None, since the checks above only work for focus becoming\n        // a different window.\n        if focused_id.is_none() && ipc_focused_id.is_some() {\n            events.push(Event::WindowFocusChanged { id: None });\n        }\n\n        for event in events {\n            state.apply(event.clone());\n            server.send_event(event);\n        }\n    }\n\n    pub fn ipc_refresh_overview(&mut self) {\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n\n        let mut state = server.event_stream_state.borrow_mut();\n        let state = &mut state.overview;\n        let is_open = self.niri.layout.is_overview_open();\n\n        if state.is_open == is_open {\n            return;\n        }\n\n        let event = Event::OverviewOpenedOrClosed { is_open };\n        state.apply(event.clone());\n        server.send_event(event);\n    }\n\n    pub fn ipc_refresh_casts(&mut self) {\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n\n        let _span = tracy_client::span!(\"State::ipc_refresh_casts\");\n\n        let mut state = server.event_stream_state.borrow_mut();\n        let state = &mut state.casts;\n\n        let mut events = Vec::new();\n        let mut seen = HashSet::new();\n\n        // Check PipeWire screencasts.\n        #[cfg(feature = \"xdp-gnome-screencast\")]\n        {\n            // Check pending dynamic casts.\n            for pending in &self.niri.casting.pending_dynamic_casts {\n                let stream_id = pending.stream_id.get();\n                seen.insert(stream_id);\n\n                // Pending dynamic casts don't change any properties, so we only need to check if\n                // it's missing from the state.\n                if !state.casts.contains_key(&stream_id) {\n                    let cast = niri_ipc::Cast {\n                        session_id: pending.session_id.get(),\n                        stream_id,\n                        kind: niri_ipc::CastKind::PipeWire,\n                        target: niri_ipc::CastTarget::Nothing {},\n                        is_dynamic_target: true,\n                        is_active: false,\n                        pid: None,\n                        pw_node_id: None,\n                    };\n                    events.push(Event::CastStartedOrChanged { cast });\n                }\n            }\n\n            // Check active casts.\n            for cast in &self.niri.casting.casts {\n                let stream_id = cast.stream_id.get();\n                seen.insert(stream_id);\n\n                let pw_node_id = cast.node_id();\n                if state.casts.get(&stream_id).is_none_or(|existing| {\n                    // Only these properties can change.\n                    existing.is_active != cast.is_active()\n                        || !cast.target.matches(&existing.target)\n                        || existing.pw_node_id != pw_node_id\n                }) {\n                    let cast = niri_ipc::Cast {\n                        session_id: cast.session_id.get(),\n                        stream_id,\n                        kind: niri_ipc::CastKind::PipeWire,\n                        target: cast.target.make_ipc(),\n                        is_dynamic_target: cast.dynamic_target,\n                        is_active: cast.is_active(),\n                        pid: None,\n                        pw_node_id,\n                    };\n                    events.push(Event::CastStartedOrChanged { cast });\n                }\n            }\n        }\n\n        // Check screencopy casts.\n        //\n        // First, clear expired casts. Ideally we'd have a deadline timer, but our 1 second frame\n        // callback timer calls refresh regularly, so that's fine as is.\n        self.niri.screencopy_state.clear_expired_casts();\n\n        for queue in self.niri.screencopy_state.queues() {\n            if let Some(cast_info) = queue.cast() {\n                let stream_id = cast_info.stream_id.get();\n                seen.insert(stream_id);\n\n                if state.casts.get(&stream_id).is_none_or(|existing| {\n                    // Only this property can change.\n                    match &existing.target {\n                        niri_ipc::CastTarget::Output { name } => *name != cast_info.output_name,\n                        _ => true,\n                    }\n                }) {\n                    let cast = niri_ipc::Cast {\n                        session_id: cast_info.session_id.get(),\n                        stream_id,\n                        kind: niri_ipc::CastKind::WlrScreencopy,\n                        target: niri_ipc::CastTarget::Output {\n                            name: cast_info.output_name.clone(),\n                        },\n                        is_dynamic_target: false,\n                        is_active: true,\n                        pid: queue.credentials().map(|creds| creds.pid),\n                        pw_node_id: None,\n                    };\n                    events.push(Event::CastStartedOrChanged { cast });\n                }\n            }\n        }\n\n        // Check for stopped casts.\n        for stream_id in state.casts.keys() {\n            if !seen.contains(stream_id) {\n                events.push(Event::CastStopped {\n                    stream_id: *stream_id,\n                });\n            }\n        }\n\n        for event in events {\n            state.apply(event.clone());\n            server.send_event(event);\n        }\n    }\n\n    pub fn ipc_config_loaded(&mut self, failed: bool) {\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n        let mut state = server.event_stream_state.borrow_mut();\n\n        let event = Event::ConfigLoaded { failed };\n        state.apply(event.clone());\n        server.send_event(event);\n    }\n\n    pub fn ipc_screenshot_taken(&mut self, path: Option<String>) {\n        let Some(server) = &self.niri.ipc_server else {\n            return;\n        };\n        let mut state = server.event_stream_state.borrow_mut();\n\n        let event = Event::ScreenshotCaptured { path };\n        state.apply(event.clone());\n        server.send_event(event);\n    }\n}\n"
  },
  {
    "path": "src/layer/mapped.rs",
    "content": "use niri_config::utils::MergeWith as _;\nuse niri_config::{Config, LayerRule};\nuse smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;\nuse smithay::backend::renderer::element::Kind;\nuse smithay::desktop::{LayerSurface, PopupManager};\nuse smithay::utils::{Logical, Point, Scale, Size};\nuse smithay::wayland::shell::wlr_layer::{ExclusiveZone, Layer};\n\nuse super::ResolvedLayerRules;\nuse crate::animation::Clock;\nuse crate::layout::shadow::Shadow;\nuse crate::niri_render_elements;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::shadow::ShadowRenderElement;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::surface::push_elements_from_surface_tree;\nuse crate::render_helpers::RenderTarget;\nuse crate::utils::{baba_is_float_offset, round_logical_in_physical};\n\n#[derive(Debug)]\npub struct MappedLayer {\n    /// The surface itself.\n    surface: LayerSurface,\n\n    /// Up-to-date rules.\n    rules: ResolvedLayerRules,\n\n    /// Buffer to draw instead of the surface when it should be blocked out.\n    block_out_buffer: SolidColorBuffer,\n\n    /// The shadow around the surface.\n    shadow: Shadow,\n\n    /// The view size for the layer surface's output.\n    view_size: Size<f64, Logical>,\n\n    /// Scale of the output the layer surface is on (and rounds its sizes to).\n    scale: f64,\n\n    /// Clock for driving animations.\n    clock: Clock,\n}\n\nniri_render_elements! {\n    LayerSurfaceRenderElement<R> => {\n        Wayland = WaylandSurfaceRenderElement<R>,\n        SolidColor = SolidColorRenderElement,\n        Shadow = ShadowRenderElement,\n    }\n}\n\nimpl MappedLayer {\n    pub fn new(\n        surface: LayerSurface,\n        rules: ResolvedLayerRules,\n        view_size: Size<f64, Logical>,\n        scale: f64,\n        clock: Clock,\n        config: &Config,\n    ) -> Self {\n        let mut shadow_config = config.layout.shadow;\n        // Shadows for layer surfaces need to be explicitly enabled.\n        shadow_config.on = false;\n        shadow_config.merge_with(&rules.shadow);\n\n        Self {\n            surface,\n            rules,\n            block_out_buffer: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),\n            view_size,\n            scale,\n            shadow: Shadow::new(shadow_config),\n            clock,\n        }\n    }\n\n    pub fn update_config(&mut self, config: &Config) {\n        let mut shadow_config = config.layout.shadow;\n        // Shadows for layer surfaces need to be explicitly enabled.\n        shadow_config.on = false;\n        shadow_config.merge_with(&self.rules.shadow);\n        self.shadow.update_config(shadow_config);\n    }\n\n    pub fn update_shaders(&mut self) {\n        self.shadow.update_shaders();\n    }\n\n    pub fn update_sizes(&mut self, view_size: Size<f64, Logical>, scale: f64) {\n        self.view_size = view_size;\n        self.scale = scale;\n    }\n\n    pub fn update_render_elements(&mut self, size: Size<f64, Logical>) {\n        // Round to physical pixels.\n        let size = size\n            .to_physical_precise_round(self.scale)\n            .to_logical(self.scale);\n\n        self.block_out_buffer.resize(size);\n\n        let radius = self.rules.geometry_corner_radius.unwrap_or_default();\n        // FIXME: is_active based on keyboard focus?\n        self.shadow\n            .update_render_elements(size, true, radius, self.scale, 1.);\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        self.rules.baba_is_float\n    }\n\n    pub fn surface(&self) -> &LayerSurface {\n        &self.surface\n    }\n\n    pub fn rules(&self) -> &ResolvedLayerRules {\n        &self.rules\n    }\n\n    /// Recomputes the resolved layer rules and returns whether they changed.\n    pub fn recompute_layer_rules(&mut self, rules: &[LayerRule], is_at_startup: bool) -> bool {\n        let new_rules = ResolvedLayerRules::compute(rules, &self.surface, is_at_startup);\n        if new_rules == self.rules {\n            return false;\n        }\n\n        self.rules = new_rules;\n        true\n    }\n\n    pub fn place_within_backdrop(&self) -> bool {\n        if !self.rules.place_within_backdrop {\n            return false;\n        }\n\n        if self.surface.layer() != Layer::Background {\n            return false;\n        }\n\n        let state = self.surface.cached_state();\n        if state.exclusive_zone != ExclusiveZone::DontCare {\n            return false;\n        }\n\n        true\n    }\n\n    pub fn bob_offset(&self) -> Point<f64, Logical> {\n        if !self.rules.baba_is_float {\n            return Point::from((0., 0.));\n        }\n\n        let y = baba_is_float_offset(self.clock.now(), self.view_size.h);\n        let y = round_logical_in_physical(self.scale, y);\n        Point::from((0., y))\n    }\n\n    pub fn render_normal<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        target: RenderTarget,\n        push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),\n    ) {\n        let scale = Scale::from(self.scale);\n        let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.);\n        let location = location + self.bob_offset();\n\n        if target.should_block_out(self.rules.block_out_from) {\n            // Round to physical pixels.\n            let location = location.to_physical_precise_round(scale).to_logical(scale);\n\n            // FIXME: take geometry-corner-radius into account.\n            let elem = SolidColorRenderElement::from_buffer(\n                &self.block_out_buffer,\n                location,\n                alpha,\n                Kind::Unspecified,\n            );\n            push(elem.into());\n        } else {\n            // Layer surfaces don't have extra geometry like windows.\n            let buf_pos = location;\n\n            let surface = self.surface.wl_surface();\n            push_elements_from_surface_tree(\n                renderer,\n                surface,\n                buf_pos.to_physical_precise_round(scale),\n                scale,\n                alpha,\n                Kind::ScanoutCandidate,\n                &mut |elem| push(elem.into()),\n            );\n        }\n\n        let location = location.to_physical_precise_round(scale).to_logical(scale);\n        self.shadow\n            .render(renderer, location, &mut |elem| push(elem.into()));\n    }\n\n    pub fn render_popups<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        target: RenderTarget,\n        push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),\n    ) {\n        let scale = Scale::from(self.scale);\n        let alpha = self.rules.opacity.unwrap_or(1.).clamp(0., 1.);\n        let location = location + self.bob_offset();\n\n        if target.should_block_out(self.rules.block_out_from) {\n            return;\n        }\n\n        // Layer surfaces don't have extra geometry like windows.\n        let buf_pos = location;\n\n        let surface = self.surface.wl_surface();\n        for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {\n            // Layer surfaces don't have extra geometry like windows.\n            let offset = popup_offset - popup.geometry().loc;\n\n            push_elements_from_surface_tree(\n                renderer,\n                popup.wl_surface(),\n                (buf_pos + offset.to_f64()).to_physical_precise_round(scale),\n                scale,\n                alpha,\n                Kind::ScanoutCandidate,\n                &mut |elem| push(elem.into()),\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/layer/mod.rs",
    "content": "use niri_config::layer_rule::{LayerRule, Match};\nuse niri_config::utils::MergeWith as _;\nuse niri_config::{BlockOutFrom, CornerRadius, ShadowRule};\nuse smithay::desktop::LayerSurface;\n\npub mod mapped;\npub use mapped::MappedLayer;\n\n/// Rules fully resolved for a layer-shell surface.\n#[derive(Debug, Default, PartialEq)]\npub struct ResolvedLayerRules {\n    /// Extra opacity to draw this layer surface with.\n    pub opacity: Option<f32>,\n\n    /// Whether to block out this layer surface from certain render targets.\n    pub block_out_from: Option<BlockOutFrom>,\n\n    /// Shadow overrides.\n    pub shadow: ShadowRule,\n\n    /// Corner radius to assume this layer surface has.\n    pub geometry_corner_radius: Option<CornerRadius>,\n\n    /// Whether to place this layer surface within the overview backdrop.\n    pub place_within_backdrop: bool,\n\n    /// Whether to bob this window up and down.\n    pub baba_is_float: bool,\n}\n\nimpl ResolvedLayerRules {\n    pub fn compute(rules: &[LayerRule], surface: &LayerSurface, is_at_startup: bool) -> Self {\n        let _span = tracy_client::span!(\"ResolvedLayerRules::compute\");\n\n        let mut resolved = ResolvedLayerRules::default();\n\n        for rule in rules {\n            let matches = |m: &Match| {\n                if let Some(at_startup) = m.at_startup {\n                    if at_startup != is_at_startup {\n                        return false;\n                    }\n                }\n\n                surface_matches(surface, m)\n            };\n\n            if !(rule.matches.is_empty() || rule.matches.iter().any(matches)) {\n                continue;\n            }\n\n            if rule.excludes.iter().any(matches) {\n                continue;\n            }\n\n            if let Some(x) = rule.opacity {\n                resolved.opacity = Some(x);\n            }\n            if let Some(x) = rule.block_out_from {\n                resolved.block_out_from = Some(x);\n            }\n            if let Some(x) = rule.geometry_corner_radius {\n                resolved.geometry_corner_radius = Some(x);\n            }\n            if let Some(x) = rule.place_within_backdrop {\n                resolved.place_within_backdrop = x;\n            }\n            if let Some(x) = rule.baba_is_float {\n                resolved.baba_is_float = x;\n            }\n\n            resolved.shadow.merge_with(&rule.shadow);\n        }\n\n        resolved\n    }\n}\n\nfn surface_matches(surface: &LayerSurface, m: &Match) -> bool {\n    if let Some(namespace_re) = &m.namespace {\n        if !namespace_re.0.is_match(surface.namespace()) {\n            return false;\n        }\n    }\n\n    true\n}\n"
  },
  {
    "path": "src/layout/closing_window.rs",
    "content": "use std::collections::HashMap;\nuse std::rc::Rc;\n\nuse anyhow::Context as _;\nuse glam::{Mat3, Vec2};\nuse niri_config::BlockOutFrom;\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::renderer::element::utils::{\n    Relocate, RelocateRenderElement, RescaleRenderElement,\n};\nuse smithay::backend::renderer::element::{Kind, RenderElement};\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture, Uniform};\nuse smithay::backend::renderer::Texture;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform};\nuse smithay::wayland::compositor::{Blocker, BlockerState};\n\nuse crate::animation::Animation;\nuse crate::niri_render_elements;\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::shader_element::ShaderRenderElement;\nuse crate::render_helpers::shaders::{mat3_uniform, ProgramType, Shaders};\nuse crate::render_helpers::snapshot::RenderSnapshot;\nuse crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};\nuse crate::render_helpers::{render_to_encompassing_texture, RenderTarget};\nuse crate::utils::transaction::TransactionBlocker;\n\n#[derive(Debug)]\npub struct ClosingWindow {\n    /// Contents of the window.\n    buffer: TextureBuffer<GlesTexture>,\n\n    /// Blocked-out contents of the window.\n    blocked_out_buffer: TextureBuffer<GlesTexture>,\n\n    /// Where the window should be blocked out from.\n    block_out_from: Option<BlockOutFrom>,\n\n    /// Size of the window geometry.\n    geo_size: Size<f64, Logical>,\n\n    /// Position in the workspace.\n    pos: Point<f64, Logical>,\n\n    /// How much the texture should be offset.\n    buffer_offset: Point<f64, Logical>,\n\n    /// How much the blocked-out texture should be offset.\n    blocked_out_buffer_offset: Point<f64, Logical>,\n\n    /// The closing animation.\n    anim_state: AnimationState,\n\n    /// Random seed for the shader.\n    random_seed: f32,\n}\n\nniri_render_elements! {\n    ClosingWindowRenderElement => {\n        Texture = RelocateRenderElement<RescaleRenderElement<PrimaryGpuTextureRenderElement>>,\n        Shader = ShaderRenderElement,\n    }\n}\n\n#[derive(Debug)]\nenum AnimationState {\n    Waiting {\n        /// Blocker for a transaction before starting the animation.\n        blocker: TransactionBlocker,\n        anim: Animation,\n    },\n    Animating(Animation),\n}\n\nimpl AnimationState {\n    pub fn new(blocker: TransactionBlocker, anim: Animation) -> Self {\n        if blocker.state() == BlockerState::Pending {\n            Self::Waiting { blocker, anim }\n        } else {\n            // This actually doesn't normally happen because the window is removed only after the\n            // closing animation is created. Though, it does happen with disable-transactions debug\n            // flag.\n            Self::Animating(anim)\n        }\n    }\n}\n\nimpl ClosingWindow {\n    pub fn new<E: RenderElement<GlesRenderer>>(\n        renderer: &mut GlesRenderer,\n        snapshot: RenderSnapshot<E, E>,\n        scale: Scale<f64>,\n        geo_size: Size<f64, Logical>,\n        pos: Point<f64, Logical>,\n        blocker: TransactionBlocker,\n        anim: Animation,\n    ) -> anyhow::Result<Self> {\n        let _span = tracy_client::span!(\"ClosingWindow::new\");\n\n        let mut render_to_texture = |elements: Vec<E>| -> anyhow::Result<_> {\n            let (texture, _sync_point, geo) = render_to_encompassing_texture(\n                renderer,\n                scale,\n                Transform::Normal,\n                Fourcc::Abgr8888,\n                &elements,\n            )\n            .context(\"error rendering to texture\")?;\n\n            let buffer = TextureBuffer::from_texture(\n                renderer,\n                texture,\n                scale,\n                Transform::Normal,\n                Vec::new(),\n            );\n\n            let offset = geo.loc.to_f64().to_logical(scale);\n\n            Ok((buffer, offset))\n        };\n\n        let (buffer, buffer_offset) =\n            render_to_texture(snapshot.contents).context(\"error rendering contents\")?;\n        let (blocked_out_buffer, blocked_out_buffer_offset) =\n            render_to_texture(snapshot.blocked_out_contents)\n                .context(\"error rendering blocked-out contents\")?;\n\n        Ok(Self {\n            buffer,\n            blocked_out_buffer,\n            block_out_from: snapshot.block_out_from,\n            geo_size,\n            pos,\n            buffer_offset,\n            blocked_out_buffer_offset,\n            anim_state: AnimationState::new(blocker, anim),\n            random_seed: fastrand::f32(),\n        })\n    }\n\n    pub fn advance_animations(&mut self) {\n        match &mut self.anim_state {\n            AnimationState::Waiting { blocker, anim } => {\n                if blocker.state() != BlockerState::Pending {\n                    let anim = anim.restarted(0., 1., 0.);\n                    self.anim_state = AnimationState::Animating(anim);\n                }\n            }\n            AnimationState::Animating(_anim) => (),\n        }\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        match &self.anim_state {\n            AnimationState::Waiting { .. } => true,\n            AnimationState::Animating(anim) => !anim.is_done(),\n        }\n    }\n\n    pub fn render(\n        &self,\n        renderer: &mut GlesRenderer,\n        view_rect: Rectangle<f64, Logical>,\n        scale: Scale<f64>,\n        target: RenderTarget,\n    ) -> ClosingWindowRenderElement {\n        let (buffer, offset) = if target.should_block_out(self.block_out_from) {\n            (&self.blocked_out_buffer, self.blocked_out_buffer_offset)\n        } else {\n            (&self.buffer, self.buffer_offset)\n        };\n\n        let anim = match &self.anim_state {\n            AnimationState::Waiting { .. } => {\n                let elem = TextureRenderElement::from_texture_buffer(\n                    buffer.clone(),\n                    Point::from((0., 0.)),\n                    1.,\n                    None,\n                    None,\n                    Kind::Unspecified,\n                );\n\n                let elem = PrimaryGpuTextureRenderElement(elem);\n                let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), 1.);\n\n                let mut location = self.pos + offset;\n                location.x -= view_rect.loc.x;\n                let elem = RelocateRenderElement::from_element(\n                    elem,\n                    location.to_physical_precise_round(scale),\n                    Relocate::Relative,\n                );\n\n                return elem.into();\n            }\n            AnimationState::Animating(anim) => anim,\n        };\n\n        let progress = anim.value();\n        let clamped_progress = anim.clamped_value().clamp(0., 1.);\n\n        if Shaders::get(renderer).program(ProgramType::Close).is_some() {\n            let area_loc = Vec2::new(view_rect.loc.x as f32, view_rect.loc.y as f32);\n            let area_size = Vec2::new(view_rect.size.w as f32, view_rect.size.h as f32);\n\n            // Round to physical pixels relative to the view position. This is similar to what\n            // happens when rendering normal windows.\n            let relative = self.pos - view_rect.loc;\n            let pos = view_rect.loc + relative.to_physical_precise_round(scale).to_logical(scale);\n\n            let geo_loc = Vec2::new(pos.x as f32, pos.y as f32);\n            let geo_size = Vec2::new(self.geo_size.w as f32, self.geo_size.h as f32);\n\n            let input_to_geo = Mat3::from_scale(area_size / geo_size)\n                * Mat3::from_translation((area_loc - geo_loc) / area_size);\n\n            let tex_scale = self.buffer.texture_scale();\n            let tex_scale = Vec2::new(tex_scale.x as f32, tex_scale.y as f32);\n            let tex_loc = Vec2::new(offset.x as f32, offset.y as f32);\n            let tex_size = self.buffer.texture().size();\n            let tex_size = Vec2::new(tex_size.w as f32, tex_size.h as f32) / tex_scale;\n\n            let geo_to_tex =\n                Mat3::from_translation(-tex_loc / tex_size) * Mat3::from_scale(geo_size / tex_size);\n\n            return ShaderRenderElement::new(\n                ProgramType::Close,\n                view_rect.size,\n                None,\n                scale.x as f32,\n                1.,\n                Rc::new([\n                    mat3_uniform(\"niri_input_to_geo\", input_to_geo),\n                    Uniform::new(\"niri_geo_size\", geo_size.to_array()),\n                    mat3_uniform(\"niri_geo_to_tex\", geo_to_tex),\n                    Uniform::new(\"niri_progress\", progress as f32),\n                    Uniform::new(\"niri_clamped_progress\", clamped_progress as f32),\n                    Uniform::new(\"niri_random_seed\", self.random_seed),\n                ]),\n                HashMap::from([(String::from(\"niri_tex\"), buffer.texture().clone())]),\n                Kind::Unspecified,\n            )\n            .with_location(Point::from((0., 0.)))\n            .into();\n        }\n\n        let elem = TextureRenderElement::from_texture_buffer(\n            buffer.clone(),\n            Point::from((0., 0.)),\n            1. - clamped_progress as f32,\n            None,\n            None,\n            Kind::Unspecified,\n        );\n\n        let elem = PrimaryGpuTextureRenderElement(elem);\n\n        let center = self.geo_size.to_point().downscale(2.);\n        let elem = RescaleRenderElement::from_element(\n            elem,\n            (center - offset).to_physical_precise_round(scale),\n            ((1. - clamped_progress) / 5. + 0.8).max(0.),\n        );\n\n        let mut location = self.pos + offset;\n        location.x -= view_rect.loc.x;\n        let elem = RelocateRenderElement::from_element(\n            elem,\n            location.to_physical_precise_round(scale),\n            Relocate::Relative,\n        );\n\n        elem.into()\n    }\n}\n"
  },
  {
    "path": "src/layout/floating.rs",
    "content": "use std::cmp::max;\nuse std::iter::zip;\nuse std::rc::Rc;\n\nuse niri_config::utils::MergeWith as _;\nuse niri_config::{PresetSize, RelativeTo};\nuse niri_ipc::{PositionChange, SizeChange, WindowLayout};\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size};\n\nuse super::closing_window::{ClosingWindow, ClosingWindowRenderElement};\nuse super::scrolling::ColumnWidth;\nuse super::tile::{Tile, TileRenderElement, TileRenderSnapshot};\nuse super::workspace::{InteractiveResize, ResolvedSize};\nuse super::{\n    ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile, SizeFrac,\n};\nuse crate::animation::{Animation, Clock};\nuse crate::niri_render_elements;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::RenderTarget;\nuse crate::utils::transaction::TransactionBlocker;\nuse crate::utils::{\n    center_preferring_top_left_in_area, clamp_preferring_top_left_in_area, ensure_min_max_size,\n    ensure_min_max_size_maybe_zero, ResizeEdge,\n};\nuse crate::window::ResolvedWindowRules;\n\n/// By how many logical pixels the directional move commands move floating windows.\npub const DIRECTIONAL_MOVE_PX: f64 = 50.;\n\n/// Space for floating windows.\n#[derive(Debug)]\npub struct FloatingSpace<W: LayoutElement> {\n    /// Tiles in top-to-bottom order.\n    tiles: Vec<Tile<W>>,\n\n    /// Extra per-tile data.\n    data: Vec<Data>,\n\n    /// Id of the active window.\n    ///\n    /// The active window is not necessarily the topmost window. Focus-follows-mouse should\n    /// activate a window, but not bring it to the top, because that's very annoying.\n    ///\n    /// This is always set to `Some()` when `tiles` isn't empty.\n    active_window_id: Option<W::Id>,\n\n    /// Ongoing interactive resize.\n    interactive_resize: Option<InteractiveResize<W>>,\n\n    /// Windows in the closing animation.\n    closing_windows: Vec<ClosingWindow>,\n\n    /// View size for this space.\n    view_size: Size<f64, Logical>,\n\n    /// Working area for this space.\n    working_area: Rectangle<f64, Logical>,\n\n    /// Scale of the output the space is on (and rounds its sizes to).\n    scale: f64,\n\n    /// Clock for driving animations.\n    clock: Clock,\n\n    /// Configurable properties of the layout.\n    options: Rc<Options>,\n}\n\nniri_render_elements! {\n    FloatingSpaceRenderElement<R> => {\n        Tile = TileRenderElement<R>,\n        ClosingWindow = ClosingWindowRenderElement,\n    }\n}\n\n/// Extra per-tile data.\n#[derive(Debug, Clone, Copy, PartialEq)]\nstruct Data {\n    /// Position relative to the working area.\n    pos: Point<f64, SizeFrac>,\n\n    /// Cached position in logical coordinates.\n    ///\n    /// Not rounded to physical pixels.\n    logical_pos: Point<f64, Logical>,\n\n    /// Cached actual size of the tile.\n    size: Size<f64, Logical>,\n\n    /// Working area used for conversions.\n    working_area: Rectangle<f64, Logical>,\n}\n\nimpl Data {\n    pub fn new<W: LayoutElement>(\n        working_area: Rectangle<f64, Logical>,\n        tile: &Tile<W>,\n        logical_pos: Point<f64, Logical>,\n    ) -> Self {\n        let mut rv = Self {\n            pos: Point::default(),\n            logical_pos: Point::default(),\n            size: Size::default(),\n            working_area,\n        };\n        rv.update(tile);\n        rv.set_logical_pos(logical_pos);\n        rv\n    }\n\n    pub fn scale_by_working_area(\n        area: Rectangle<f64, Logical>,\n        pos: Point<f64, SizeFrac>,\n    ) -> Point<f64, Logical> {\n        let mut logical_pos = Point::from((pos.x, pos.y));\n        logical_pos.x *= area.size.w;\n        logical_pos.y *= area.size.h;\n        logical_pos += area.loc;\n        logical_pos\n    }\n\n    pub fn logical_to_size_frac_in_working_area(\n        area: Rectangle<f64, Logical>,\n        logical_pos: Point<f64, Logical>,\n    ) -> Point<f64, SizeFrac> {\n        let pos = logical_pos - area.loc;\n        let mut pos = Point::from((pos.x, pos.y));\n        pos.x /= f64::max(area.size.w, 1.0);\n        pos.y /= f64::max(area.size.h, 1.0);\n        pos\n    }\n\n    fn recompute_logical_pos(&mut self) {\n        let mut logical_pos = Self::scale_by_working_area(self.working_area, self.pos);\n\n        // Make sure the window doesn't go too much off-screen. Numbers taken from Mutter.\n        let min_on_screen_hor = f64::clamp(self.size.w / 4., 10., 75.);\n        let min_on_screen_ver = f64::clamp(self.size.h / 4., 10., 75.);\n        let max_off_screen_hor = f64::max(0., self.size.w - min_on_screen_hor);\n        let max_off_screen_ver = f64::max(0., self.size.h - min_on_screen_ver);\n\n        logical_pos -= self.working_area.loc;\n        logical_pos.x = f64::max(logical_pos.x, -max_off_screen_hor);\n        logical_pos.y = f64::max(logical_pos.y, -max_off_screen_ver);\n        logical_pos.x = f64::min(\n            logical_pos.x,\n            self.working_area.size.w - self.size.w + max_off_screen_hor,\n        );\n        logical_pos.y = f64::min(\n            logical_pos.y,\n            self.working_area.size.h - self.size.h + max_off_screen_ver,\n        );\n        logical_pos += self.working_area.loc;\n\n        self.logical_pos = logical_pos;\n    }\n\n    pub fn update_config(&mut self, working_area: Rectangle<f64, Logical>) {\n        if self.working_area == working_area {\n            return;\n        }\n\n        self.working_area = working_area;\n        self.recompute_logical_pos();\n    }\n\n    pub fn update<W: LayoutElement>(&mut self, tile: &Tile<W>) {\n        let size = tile.tile_size();\n        if self.size == size {\n            return;\n        }\n\n        self.size = size;\n        self.recompute_logical_pos();\n    }\n\n    pub fn set_logical_pos(&mut self, logical_pos: Point<f64, Logical>) {\n        self.pos = Self::logical_to_size_frac_in_working_area(self.working_area, logical_pos);\n\n        // This will clamp the logical position to the current working area.\n        self.recompute_logical_pos();\n    }\n\n    pub fn center(&self) -> Point<f64, Logical> {\n        self.logical_pos + self.size.downscale(2.)\n    }\n\n    #[cfg(test)]\n    fn verify_invariants(&self) {\n        let mut temp = *self;\n        temp.recompute_logical_pos();\n        assert_eq!(\n            self.logical_pos, temp.logical_pos,\n            \"cached logical pos must be up to date\"\n        );\n    }\n}\n\nimpl<W: LayoutElement> FloatingSpace<W> {\n    pub fn new(\n        view_size: Size<f64, Logical>,\n        working_area: Rectangle<f64, Logical>,\n        scale: f64,\n        clock: Clock,\n        options: Rc<Options>,\n    ) -> Self {\n        Self {\n            tiles: Vec::new(),\n            data: Vec::new(),\n            active_window_id: None,\n            interactive_resize: None,\n            closing_windows: Vec::new(),\n            view_size,\n            working_area,\n            scale,\n            clock,\n            options,\n        }\n    }\n\n    pub fn update_config(\n        &mut self,\n        view_size: Size<f64, Logical>,\n        working_area: Rectangle<f64, Logical>,\n        scale: f64,\n        options: Rc<Options>,\n    ) {\n        for (tile, data) in zip(&mut self.tiles, &mut self.data) {\n            tile.update_config(view_size, scale, options.clone());\n            data.update(tile);\n            data.update_config(working_area);\n        }\n\n        self.view_size = view_size;\n        self.working_area = working_area;\n        self.scale = scale;\n        self.options = options;\n    }\n\n    pub fn update_shaders(&mut self) {\n        for tile in &mut self.tiles {\n            tile.update_shaders();\n        }\n    }\n\n    pub fn advance_animations(&mut self) {\n        for tile in &mut self.tiles {\n            tile.advance_animations();\n        }\n\n        self.closing_windows.retain_mut(|closing| {\n            closing.advance_animations();\n            closing.are_animations_ongoing()\n        });\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        self.tiles.iter().any(Tile::are_animations_ongoing) || !self.closing_windows.is_empty()\n    }\n\n    pub fn are_transitions_ongoing(&self) -> bool {\n        self.tiles.iter().any(Tile::are_transitions_ongoing) || !self.closing_windows.is_empty()\n    }\n\n    pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {\n        let active = self.active_window_id.clone();\n        for (tile, offset) in self.tiles_with_offsets_mut() {\n            let id = tile.window().id();\n            let is_active = is_active && Some(id) == active.as_ref();\n\n            let mut tile_view_rect = view_rect;\n            tile_view_rect.loc -= offset + tile.render_offset();\n            tile.update_render_elements(is_active, tile_view_rect);\n        }\n    }\n\n    pub fn tiles(&self) -> impl Iterator<Item = &Tile<W>> + '_ {\n        self.tiles.iter()\n    }\n\n    pub fn tiles_mut(&mut self) -> impl Iterator<Item = &mut Tile<W>> + '_ {\n        self.tiles.iter_mut()\n    }\n\n    pub fn tiles_with_offsets(&self) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>)> + '_ {\n        let offsets = self.data.iter().map(|d| d.logical_pos);\n        zip(&self.tiles, offsets)\n    }\n\n    pub fn tiles_with_offsets_mut(\n        &mut self,\n    ) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> + '_ {\n        let offsets = self.data.iter().map(|d| d.logical_pos);\n        zip(&mut self.tiles, offsets)\n    }\n\n    pub fn tiles_with_render_positions(\n        &self,\n    ) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>)> {\n        let scale = self.scale;\n        self.tiles_with_offsets().map(move |(tile, offset)| {\n            let pos = offset + tile.render_offset();\n            // Round to physical pixels.\n            let pos = pos.to_physical_precise_round(scale).to_logical(scale);\n            (tile, pos)\n        })\n    }\n\n    pub fn tiles_with_render_positions_mut(\n        &mut self,\n        round: bool,\n    ) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> {\n        let scale = self.scale;\n        self.tiles_with_offsets_mut().map(move |(tile, offset)| {\n            let mut pos = offset + tile.render_offset();\n            // Round to physical pixels.\n            if round {\n                pos = pos.to_physical_precise_round(scale).to_logical(scale);\n            }\n            (tile, pos)\n        })\n    }\n\n    pub fn tiles_with_ipc_layouts(&self) -> impl Iterator<Item = (&Tile<W>, WindowLayout)> {\n        let scale = self.scale;\n        self.tiles_with_offsets().map(move |(tile, offset)| {\n            // Do not include animated render offset here to avoid IPC spam.\n            let pos = offset;\n            // Round to physical pixels.\n            let pos = pos.to_physical_precise_round(scale).to_logical(scale);\n\n            let layout = WindowLayout {\n                tile_pos_in_workspace_view: Some(pos.into()),\n                ..tile.ipc_layout_template()\n            };\n            (tile, layout)\n        })\n    }\n\n    pub fn new_window_toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> {\n        let border_config = self.options.layout.border.merged_with(&rules.border);\n        compute_toplevel_bounds(border_config, self.working_area.size)\n    }\n\n    /// Returns the geometry of the active tile relative to and clamped to the working area.\n    ///\n    /// During animations, assumes the final tile position.\n    pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {\n        let (tile, offset) = self.tiles_with_offsets().next()?;\n\n        let tile_size = tile.tile_size();\n        let tile_rect = Rectangle::new(offset, tile_size);\n\n        self.working_area.intersection(tile_rect)\n    }\n\n    pub fn popup_target_rect(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {\n        for (tile, pos) in self.tiles_with_offsets() {\n            if tile.window().id() == id {\n                // Position within the working area.\n                let mut target = self.working_area;\n                target.loc -= pos;\n                target.loc -= tile.window_loc();\n\n                return Some(target);\n            }\n        }\n        None\n    }\n\n    fn idx_of(&self, id: &W::Id) -> Option<usize> {\n        self.tiles.iter().position(|tile| tile.window().id() == id)\n    }\n\n    fn contains(&self, id: &W::Id) -> bool {\n        self.idx_of(id).is_some()\n    }\n\n    pub fn active_window(&self) -> Option<&W> {\n        let id = self.active_window_id.as_ref()?;\n        self.tiles\n            .iter()\n            .find(|tile| tile.window().id() == id)\n            .map(Tile::window)\n    }\n\n    pub fn active_window_mut(&mut self) -> Option<&mut W> {\n        let id = self.active_window_id.as_ref()?;\n        self.tiles\n            .iter_mut()\n            .find(|tile| tile.window().id() == id)\n            .map(Tile::window_mut)\n    }\n\n    pub fn has_window(&self, id: &W::Id) -> bool {\n        self.tiles.iter().any(|tile| tile.window().id() == id)\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.tiles.is_empty()\n    }\n\n    pub fn add_tile(&mut self, tile: Tile<W>, activate: bool) {\n        self.add_tile_at(0, tile, activate);\n    }\n\n    fn add_tile_at(&mut self, mut idx: usize, mut tile: Tile<W>, activate: bool) {\n        tile.update_config(self.view_size, self.scale, self.options.clone());\n\n        // Restore the previous floating window size, and in case the tile is fullscreen,\n        // unfullscreen it.\n        let floating_size = tile.floating_window_size;\n        let win = tile.window_mut();\n        let mut size = if !win.pending_sizing_mode().is_normal() {\n            // If the window was fullscreen or maximized without a floating size, ask for (0, 0).\n            floating_size.unwrap_or_default()\n        } else {\n            // If the window wasn't fullscreen without a floating size (e.g. it was tiled before),\n            // ask for the current size. If the current size is unknown (the window was only ever\n            // fullscreen until now), fall back to (0, 0).\n            floating_size.unwrap_or_else(|| win.expected_size().unwrap_or_default())\n        };\n\n        // Apply min/max size window rules. If requesting a concrete size, apply completely; if\n        // requesting (0, 0), apply only when min/max results in a fixed size.\n        let min_size = win.min_size();\n        let max_size = win.max_size();\n        size.w = ensure_min_max_size_maybe_zero(size.w, min_size.w, max_size.w);\n        size.h = ensure_min_max_size_maybe_zero(size.h, min_size.h, max_size.h);\n\n        win.request_size_once(size, true);\n\n        if activate || self.tiles.is_empty() {\n            self.active_window_id = Some(win.id().clone());\n        }\n\n        // Make sure the tile isn't inserted below its parent.\n        for (i, tile_above) in self.tiles.iter().enumerate().take(idx) {\n            if win.is_child_of(tile_above.window()) {\n                idx = i;\n                break;\n            }\n        }\n\n        let pos = self.stored_or_default_tile_pos(&tile).unwrap_or_else(|| {\n            center_preferring_top_left_in_area(self.working_area, tile.tile_size())\n        });\n\n        let data = Data::new(self.working_area, &tile, pos);\n        self.data.insert(idx, data);\n        self.tiles.insert(idx, tile);\n\n        self.bring_up_descendants_of(idx);\n    }\n\n    pub fn add_tile_above(&mut self, above: &W::Id, mut tile: Tile<W>, activate: bool) {\n        let idx = self.idx_of(above).unwrap();\n\n        let above_pos = self.data[idx].logical_pos;\n        let above_size = self.data[idx].size;\n        let tile_size = tile.tile_size();\n        let pos = above_pos + (above_size.to_point() - tile_size.to_point()).downscale(2.);\n        let pos = self.clamp_within_working_area(pos, tile_size);\n        tile.floating_pos = Some(self.logical_to_size_frac(pos));\n\n        self.add_tile_at(idx, tile, activate);\n    }\n\n    fn bring_up_descendants_of(&mut self, idx: usize) {\n        let tile = &self.tiles[idx];\n        let win = tile.window();\n\n        // We always maintain the correct stacking order, so walking descendants back to front\n        // should give us all of them.\n        let mut descendants: Vec<usize> = Vec::new();\n        for (i, tile_below) in self.tiles.iter().enumerate().skip(idx + 1).rev() {\n            let win_below = tile_below.window();\n            if win_below.is_child_of(win)\n                || descendants\n                    .iter()\n                    .any(|idx| win_below.is_child_of(self.tiles[*idx].window()))\n            {\n                descendants.push(i);\n            }\n        }\n\n        // Now, descendants is in back-to-front order, and repositioning them in the front-to-back\n        // order will preserve the subsequent indices and work out right.\n        let mut idx = idx;\n        for descendant_idx in descendants.into_iter().rev() {\n            self.raise_window(descendant_idx, idx);\n            idx += 1;\n        }\n    }\n\n    pub fn remove_active_tile(&mut self) -> Option<RemovedTile<W>> {\n        let id = self.active_window_id.clone()?;\n        Some(self.remove_tile(&id))\n    }\n\n    pub fn remove_tile(&mut self, id: &W::Id) -> RemovedTile<W> {\n        let idx = self.idx_of(id).unwrap();\n        self.remove_tile_by_idx(idx)\n    }\n\n    fn remove_tile_by_idx(&mut self, idx: usize) -> RemovedTile<W> {\n        let mut tile = self.tiles.remove(idx);\n        let data = self.data.remove(idx);\n\n        if self.tiles.is_empty() {\n            self.active_window_id = None;\n        } else if Some(tile.window().id()) == self.active_window_id.as_ref() {\n            // The active tile was removed, make the topmost tile active.\n            self.active_window_id = Some(self.tiles[0].window().id().clone());\n        }\n\n        // Stop interactive resize.\n        if let Some(resize) = &self.interactive_resize {\n            if tile.window().id() == &resize.window {\n                self.interactive_resize = None;\n            }\n        }\n\n        // Store the floating size if we have one.\n        if let Some(size) = tile.window().expected_size() {\n            tile.floating_window_size = Some(size);\n        }\n        // Store the floating position.\n        tile.floating_pos = Some(data.pos);\n\n        let width = ColumnWidth::Fixed(tile.tile_expected_or_current_size().w);\n        RemovedTile {\n            tile,\n            width,\n            is_full_width: false,\n            is_floating: true,\n        }\n    }\n\n    pub fn start_close_animation_for_window(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        id: &W::Id,\n        blocker: TransactionBlocker,\n    ) {\n        let (tile, tile_pos) = self\n            .tiles_with_render_positions_mut(false)\n            .find(|(tile, _)| tile.window().id() == id)\n            .unwrap();\n\n        let Some(snapshot) = tile.take_unmap_snapshot() else {\n            return;\n        };\n\n        let tile_size = tile.tile_size();\n\n        self.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);\n    }\n\n    pub fn activate_window_without_raising(&mut self, id: &W::Id) -> bool {\n        if !self.contains(id) {\n            return false;\n        }\n\n        self.active_window_id = Some(id.clone());\n        true\n    }\n\n    pub fn activate_window(&mut self, id: &W::Id) -> bool {\n        let Some(idx) = self.idx_of(id) else {\n            return false;\n        };\n\n        self.raise_window(idx, 0);\n        self.active_window_id = Some(id.clone());\n        self.bring_up_descendants_of(0);\n\n        true\n    }\n\n    fn raise_window(&mut self, from_idx: usize, to_idx: usize) {\n        assert!(to_idx <= from_idx);\n\n        let tile = self.tiles.remove(from_idx);\n        let data = self.data.remove(from_idx);\n        self.tiles.insert(to_idx, tile);\n        self.data.insert(to_idx, data);\n    }\n\n    pub fn start_close_animation_for_tile(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        snapshot: TileRenderSnapshot,\n        tile_size: Size<f64, Logical>,\n        tile_pos: Point<f64, Logical>,\n        blocker: TransactionBlocker,\n    ) {\n        let anim = Animation::new(\n            self.clock.clone(),\n            0.,\n            1.,\n            0.,\n            self.options.animations.window_close.anim,\n        );\n\n        let blocker = if self.options.disable_transactions {\n            TransactionBlocker::completed()\n        } else {\n            blocker\n        };\n\n        let scale = Scale::from(self.scale);\n        let res = ClosingWindow::new(\n            renderer, snapshot, scale, tile_size, tile_pos, blocker, anim,\n        );\n        match res {\n            Ok(closing) => {\n                self.closing_windows.push(closing);\n            }\n            Err(err) => {\n                warn!(\"error creating a closing window animation: {err:?}\");\n            }\n        }\n    }\n\n    pub fn toggle_window_width(&mut self, id: Option<&W::Id>, forwards: bool) {\n        let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else {\n            return;\n        };\n        let idx = self.idx_of(&id).unwrap();\n\n        let available_size = self.working_area.size.w;\n\n        let len = self.options.layout.preset_column_widths.len();\n        let tile = &mut self.tiles[idx];\n        let preset_idx = if let Some(idx) = tile.floating_preset_width_idx {\n            (idx + if forwards { 1 } else { len - 1 }) % len\n        } else {\n            let current_window = tile.window_expected_or_current_size().w;\n            let current_tile = tile.tile_expected_or_current_size().w;\n\n            let mut it = self\n                .options\n                .layout\n                .preset_column_widths\n                .iter()\n                .map(|preset| resolve_preset_size(*preset, available_size));\n\n            if forwards {\n                it.position(|resolved| {\n                    match resolved {\n                        // Some allowance for fractional scaling purposes.\n                        ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,\n                        ResolvedSize::Window(resolved) => current_window + 1. < resolved,\n                    }\n                })\n                .unwrap_or(0)\n            } else {\n                it.rposition(|resolved| {\n                    match resolved {\n                        // Some allowance for fractional scaling purposes.\n                        ResolvedSize::Tile(resolved) => resolved + 1. < current_tile,\n                        ResolvedSize::Window(resolved) => resolved + 1. < current_window,\n                    }\n                })\n                .unwrap_or(len - 1)\n            }\n        };\n\n        let preset = self.options.layout.preset_column_widths[preset_idx];\n        self.set_window_width(Some(&id), SizeChange::from(preset), true);\n\n        self.tiles[idx].floating_preset_width_idx = Some(preset_idx);\n\n        self.interactive_resize_end(Some(&id));\n    }\n\n    pub fn start_open_animation(&mut self, id: &W::Id) -> bool {\n        let Some(idx) = self.idx_of(id) else {\n            return false;\n        };\n\n        self.tiles[idx].start_open_animation();\n        true\n    }\n\n    pub fn toggle_window_height(&mut self, id: Option<&W::Id>, forwards: bool) {\n        let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else {\n            return;\n        };\n        let idx = self.idx_of(&id).unwrap();\n\n        let available_size = self.working_area.size.h;\n\n        let len = self.options.layout.preset_window_heights.len();\n        let tile = &mut self.tiles[idx];\n        let preset_idx = if let Some(idx) = tile.floating_preset_height_idx {\n            (idx + if forwards { 1 } else { len - 1 }) % len\n        } else {\n            let current_window = tile.window_expected_or_current_size().h;\n            let current_tile = tile.tile_expected_or_current_size().h;\n\n            let mut it = self\n                .options\n                .layout\n                .preset_window_heights\n                .iter()\n                .map(|preset| resolve_preset_size(*preset, available_size));\n\n            if forwards {\n                it.position(|resolved| {\n                    match resolved {\n                        // Some allowance for fractional scaling purposes.\n                        ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,\n                        ResolvedSize::Window(resolved) => current_window + 1. < resolved,\n                    }\n                })\n                .unwrap_or(0)\n            } else {\n                it.rposition(|resolved| {\n                    match resolved {\n                        // Some allowance for fractional scaling purposes.\n                        ResolvedSize::Tile(resolved) => resolved + 1. < current_tile,\n                        ResolvedSize::Window(resolved) => resolved + 1. < current_window,\n                    }\n                })\n                .unwrap_or(len - 1)\n            }\n        };\n\n        let preset = self.options.layout.preset_window_heights[preset_idx];\n        self.set_window_height(Some(&id), SizeChange::from(preset), true);\n\n        let tile = &mut self.tiles[idx];\n        tile.floating_preset_height_idx = Some(preset_idx);\n\n        self.interactive_resize_end(Some(&id));\n    }\n\n    pub fn set_window_width(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) {\n        let Some(id) = id.or(self.active_window_id.as_ref()) else {\n            return;\n        };\n        let idx = self.idx_of(id).unwrap();\n\n        let tile = &mut self.tiles[idx];\n        tile.floating_preset_width_idx = None;\n\n        let available_size = self.working_area.size.w;\n        let win = tile.window();\n        let current_window = win.expected_size().unwrap_or_else(|| win.size()).w;\n        let current_tile = tile.tile_expected_or_current_size().w;\n\n        const MAX_PX: f64 = 100000.;\n        const MAX_F: f64 = 10000.;\n\n        let win_width = match change {\n            SizeChange::SetFixed(win_width) => f64::from(win_width),\n            SizeChange::SetProportion(prop) => {\n                let prop = (prop / 100.).clamp(0., MAX_F);\n                let tile_width = available_size * prop;\n                tile.window_width_for_tile_width(tile_width)\n            }\n            SizeChange::AdjustFixed(delta) => f64::from(current_window.saturating_add(delta)),\n            SizeChange::AdjustProportion(delta) => {\n                let current_prop = current_tile / available_size;\n                let prop = (current_prop + delta / 100.).clamp(0., MAX_F);\n                let tile_width = available_size * prop;\n                tile.window_width_for_tile_width(tile_width)\n            }\n        };\n        let win_width = win_width.round().clamp(1., MAX_PX) as i32;\n\n        let win = tile.window_mut();\n        let min_size = win.min_size();\n        let max_size = win.max_size();\n\n        let win_width = ensure_min_max_size(win_width, min_size.w, max_size.w);\n\n        let win_height = win.expected_size().unwrap_or_default().h;\n        let win_height = ensure_min_max_size(win_height, min_size.h, max_size.h);\n\n        let win_size = Size::from((win_width, win_height));\n        win.request_size_once(win_size, animate);\n    }\n\n    pub fn set_window_height(&mut self, id: Option<&W::Id>, change: SizeChange, animate: bool) {\n        let Some(id) = id.or(self.active_window_id.as_ref()) else {\n            return;\n        };\n        let idx = self.idx_of(id).unwrap();\n\n        let tile = &mut self.tiles[idx];\n        tile.floating_preset_height_idx = None;\n\n        let available_size = self.working_area.size.h;\n        let win = tile.window();\n        let current_window = win.expected_size().unwrap_or_else(|| win.size()).h;\n        let current_tile = tile.tile_expected_or_current_size().h;\n\n        const MAX_PX: f64 = 100000.;\n        const MAX_F: f64 = 10000.;\n\n        let win_height = match change {\n            SizeChange::SetFixed(win_height) => f64::from(win_height),\n            SizeChange::SetProportion(prop) => {\n                let prop = (prop / 100.).clamp(0., MAX_F);\n                let tile_height = available_size * prop;\n                tile.window_height_for_tile_height(tile_height)\n            }\n            SizeChange::AdjustFixed(delta) => f64::from(current_window.saturating_add(delta)),\n            SizeChange::AdjustProportion(delta) => {\n                let current_prop = current_tile / available_size;\n                let prop = (current_prop + delta / 100.).clamp(0., MAX_F);\n                let tile_height = available_size * prop;\n                tile.window_height_for_tile_height(tile_height)\n            }\n        };\n        let win_height = win_height.round().clamp(1., MAX_PX) as i32;\n\n        let win = tile.window_mut();\n        let min_size = win.min_size();\n        let max_size = win.max_size();\n\n        let win_height = ensure_min_max_size(win_height, min_size.h, max_size.h);\n\n        let win_width = win.expected_size().unwrap_or_default().w;\n        let win_width = ensure_min_max_size(win_width, min_size.w, max_size.w);\n\n        let win_size = Size::from((win_width, win_height));\n        win.request_size_once(win_size, animate);\n    }\n\n    fn focus_directional(\n        &mut self,\n        distance: impl Fn(Point<f64, Logical>, Point<f64, Logical>) -> f64,\n    ) -> bool {\n        let Some(active_id) = &self.active_window_id else {\n            return false;\n        };\n        let active_idx = self.idx_of(active_id).unwrap();\n        let center = self.data[active_idx].center();\n\n        let result = zip(&self.tiles, &self.data)\n            .filter(|(tile, _)| tile.window().id() != active_id)\n            .map(|(tile, data)| (tile, distance(center, data.center())))\n            .filter(|(_, dist)| *dist > 0.)\n            .min_by(|(_, dist_a), (_, dist_b)| f64::total_cmp(dist_a, dist_b));\n        if let Some((tile, _)) = result {\n            let id = tile.window().id().clone();\n            self.activate_window(&id);\n            true\n        } else {\n            false\n        }\n    }\n\n    pub fn focus_left(&mut self) -> bool {\n        self.focus_directional(|focus, other| focus.x - other.x)\n    }\n\n    pub fn focus_right(&mut self) -> bool {\n        self.focus_directional(|focus, other| other.x - focus.x)\n    }\n\n    pub fn focus_up(&mut self) -> bool {\n        self.focus_directional(|focus, other| focus.y - other.y)\n    }\n\n    pub fn focus_down(&mut self) -> bool {\n        self.focus_directional(|focus, other| other.y - focus.y)\n    }\n\n    pub fn focus_leftmost(&mut self) {\n        let result = self\n            .tiles_with_offsets()\n            .min_by(|(_, pos_a), (_, pos_b)| f64::total_cmp(&pos_a.x, &pos_b.x));\n        if let Some((tile, _)) = result {\n            let id = tile.window().id().clone();\n            self.activate_window(&id);\n        }\n    }\n\n    pub fn focus_rightmost(&mut self) {\n        let result = self\n            .tiles_with_offsets()\n            .max_by(|(_, pos_a), (_, pos_b)| f64::total_cmp(&pos_a.x, &pos_b.x));\n        if let Some((tile, _)) = result {\n            let id = tile.window().id().clone();\n            self.activate_window(&id);\n        }\n    }\n\n    pub fn focus_topmost(&mut self) {\n        let result = self\n            .tiles_with_offsets()\n            .min_by(|(_, pos_a), (_, pos_b)| f64::total_cmp(&pos_a.y, &pos_b.y));\n        if let Some((tile, _)) = result {\n            let id = tile.window().id().clone();\n            self.activate_window(&id);\n        }\n    }\n\n    pub fn focus_bottommost(&mut self) {\n        let result = self\n            .tiles_with_offsets()\n            .max_by(|(_, pos_a), (_, pos_b)| f64::total_cmp(&pos_a.y, &pos_b.y));\n        if let Some((tile, _)) = result {\n            let id = tile.window().id().clone();\n            self.activate_window(&id);\n        }\n    }\n\n    fn move_to(&mut self, idx: usize, new_pos: Point<f64, Logical>, animate: bool) {\n        if animate {\n            self.move_and_animate(idx, new_pos);\n        } else {\n            self.data[idx].set_logical_pos(new_pos);\n        }\n\n        self.interactive_resize_end(None);\n    }\n\n    fn move_by(&mut self, amount: Point<f64, Logical>) {\n        let Some(active_id) = &self.active_window_id else {\n            return;\n        };\n        let idx = self.idx_of(active_id).unwrap();\n\n        let new_pos = self.data[idx].logical_pos + amount;\n        self.move_to(idx, new_pos, true)\n    }\n\n    pub fn move_left(&mut self) {\n        self.move_by(Point::from((-DIRECTIONAL_MOVE_PX, 0.)));\n    }\n\n    pub fn move_right(&mut self) {\n        self.move_by(Point::from((DIRECTIONAL_MOVE_PX, 0.)));\n    }\n\n    pub fn move_up(&mut self) {\n        self.move_by(Point::from((0., -DIRECTIONAL_MOVE_PX)));\n    }\n\n    pub fn move_down(&mut self) {\n        self.move_by(Point::from((0., DIRECTIONAL_MOVE_PX)));\n    }\n\n    pub fn move_window(\n        &mut self,\n        id: Option<&W::Id>,\n        x: PositionChange,\n        y: PositionChange,\n        animate: bool,\n    ) {\n        let Some(id) = id.or(self.active_window_id.as_ref()) else {\n            return;\n        };\n        let idx = self.idx_of(id).unwrap();\n\n        let mut pos = self.data[idx].logical_pos;\n\n        let available_width = self.working_area.size.w;\n        let available_height = self.working_area.size.h;\n        let working_area_loc = self.working_area.loc;\n\n        const MAX_F: f64 = 10000.;\n\n        match x {\n            PositionChange::SetFixed(x) => pos.x = x + working_area_loc.x,\n            PositionChange::SetProportion(prop) => {\n                let prop = (prop / 100.).clamp(0., MAX_F);\n                pos.x = available_width * prop + working_area_loc.x;\n            }\n            PositionChange::AdjustFixed(x) => pos.x += x,\n            PositionChange::AdjustProportion(prop) => {\n                let current_prop = (pos.x - working_area_loc.x) / available_width.max(1.);\n                let prop = (current_prop + prop / 100.).clamp(0., MAX_F);\n                pos.x = available_width * prop + working_area_loc.x;\n            }\n        }\n        match y {\n            PositionChange::SetFixed(y) => pos.y = y + working_area_loc.y,\n            PositionChange::SetProportion(prop) => {\n                let prop = (prop / 100.).clamp(0., MAX_F);\n                pos.y = available_height * prop + working_area_loc.y;\n            }\n            PositionChange::AdjustFixed(y) => pos.y += y,\n            PositionChange::AdjustProportion(prop) => {\n                let current_prop = (pos.y - working_area_loc.y) / available_height.max(1.);\n                let prop = (current_prop + prop / 100.).clamp(0., MAX_F);\n                pos.y = available_height * prop + working_area_loc.y;\n            }\n        }\n\n        self.move_to(idx, pos, animate);\n    }\n\n    pub fn center_window(&mut self, id: Option<&W::Id>) {\n        let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else {\n            return;\n        };\n        let idx = self.idx_of(&id).unwrap();\n\n        let new_pos = center_preferring_top_left_in_area(self.working_area, self.data[idx].size);\n        self.move_to(idx, new_pos, true);\n    }\n\n    pub fn descendants_added(&mut self, id: &W::Id) -> bool {\n        let Some(idx) = self.idx_of(id) else {\n            return false;\n        };\n\n        self.bring_up_descendants_of(idx);\n        true\n    }\n\n    pub fn update_window(&mut self, id: &W::Id, serial: Option<Serial>) -> bool {\n        let Some(tile_idx) = self.idx_of(id) else {\n            return false;\n        };\n\n        let tile = &mut self.tiles[tile_idx];\n        let data = &mut self.data[tile_idx];\n\n        let resize = tile.window_mut().interactive_resize_data();\n\n        // Do this before calling update_window() so it can get up-to-date info.\n        if let Some(serial) = serial {\n            tile.window_mut().on_commit(serial);\n        }\n\n        let prev_size = data.size;\n\n        tile.update_window();\n        data.update(tile);\n\n        // When resizing by top/left edge, update the position accordingly.\n        if let Some(resize) = resize {\n            let mut offset = Point::from((0., 0.));\n            if resize.edges.contains(ResizeEdge::LEFT) {\n                offset.x += prev_size.w - data.size.w;\n            }\n            if resize.edges.contains(ResizeEdge::TOP) {\n                offset.y += prev_size.h - data.size.h;\n            }\n            data.set_logical_pos(data.logical_pos + offset);\n        }\n\n        true\n    }\n\n    pub fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        view_rect: Rectangle<f64, Logical>,\n        target: RenderTarget,\n        focus_ring: bool,\n        push: &mut dyn FnMut(FloatingSpaceRenderElement<R>),\n    ) {\n        let scale = Scale::from(self.scale);\n\n        // Draw the closing windows on top of the other windows.\n        //\n        // FIXME: I guess this should rather preserve the stacking order when the window is closed.\n        for closing in self.closing_windows.iter().rev() {\n            let elem = closing.render(renderer.as_gles_renderer(), view_rect, scale, target);\n            push(elem.into());\n        }\n\n        let active = self.active_window_id.clone();\n        for (tile, tile_pos) in self.tiles_with_render_positions() {\n            // For the active tile, draw the focus ring.\n            let focus_ring = focus_ring && Some(tile.window().id()) == active.as_ref();\n\n            tile.render(renderer, tile_pos, focus_ring, target, &mut |elem| {\n                push(elem.into())\n            });\n        }\n    }\n\n    pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {\n        if self.interactive_resize.is_some() {\n            return false;\n        }\n\n        let tile = self\n            .tiles\n            .iter_mut()\n            .find(|tile| tile.window().id() == &window)\n            .unwrap();\n\n        let original_window_size = tile.window_size();\n\n        let resize = InteractiveResize {\n            window,\n            original_window_size,\n            data: InteractiveResizeData { edges },\n        };\n        self.interactive_resize = Some(resize);\n\n        true\n    }\n\n    pub fn interactive_resize_update(\n        &mut self,\n        window: &W::Id,\n        delta: Point<f64, Logical>,\n    ) -> bool {\n        let Some(resize) = &self.interactive_resize else {\n            return false;\n        };\n\n        if window != &resize.window {\n            return false;\n        }\n\n        let original_window_size = resize.original_window_size;\n        let edges = resize.data.edges;\n\n        if edges.intersects(ResizeEdge::LEFT_RIGHT) {\n            let mut dx = delta.x;\n            if edges.contains(ResizeEdge::LEFT) {\n                dx = -dx;\n            };\n\n            let window_width = (original_window_size.w + dx).round() as i32;\n            self.set_window_width(Some(window), SizeChange::SetFixed(window_width), false);\n        }\n\n        if edges.intersects(ResizeEdge::TOP_BOTTOM) {\n            let mut dy = delta.y;\n            if edges.contains(ResizeEdge::TOP) {\n                dy = -dy;\n            };\n\n            let window_height = (original_window_size.h + dy).round() as i32;\n            self.set_window_height(Some(window), SizeChange::SetFixed(window_height), false);\n        }\n\n        true\n    }\n\n    pub fn interactive_resize_end(&mut self, window: Option<&W::Id>) {\n        let Some(resize) = &self.interactive_resize else {\n            return;\n        };\n\n        if let Some(window) = window {\n            if window != &resize.window {\n                return;\n            }\n        }\n\n        self.interactive_resize = None;\n    }\n\n    pub fn refresh(&mut self, is_active: bool, is_focused: bool) {\n        let active = self.active_window_id.clone();\n        for tile in &mut self.tiles {\n            let win = tile.window_mut();\n\n            win.set_active_in_column(true);\n            win.set_floating(true);\n\n            let mut is_active = is_active && Some(win.id()) == active.as_ref();\n            if self.options.deactivate_unfocused_windows {\n                is_active &= is_focused;\n            }\n            win.set_activated(is_active);\n\n            let resize_data = self\n                .interactive_resize\n                .as_ref()\n                .filter(|resize| &resize.window == win.id())\n                .map(|resize| resize.data);\n            win.set_interactive_resize(resize_data);\n\n            let border_config = self.options.layout.border.merged_with(&win.rules().border);\n            let bounds = compute_toplevel_bounds(border_config, self.working_area.size);\n            win.set_bounds(bounds);\n\n            // If transactions are disabled, also disable combined throttling, for more\n            // intuitive behavior.\n            let intent = if self.options.disable_resize_throttling {\n                ConfigureIntent::CanSend\n            } else {\n                win.configure_intent()\n            };\n\n            if matches!(\n                intent,\n                ConfigureIntent::CanSend | ConfigureIntent::ShouldSend\n            ) {\n                win.send_pending_configure();\n            }\n\n            win.refresh();\n        }\n    }\n\n    pub fn clamp_within_working_area(\n        &self,\n        pos: Point<f64, Logical>,\n        size: Size<f64, Logical>,\n    ) -> Point<f64, Logical> {\n        let mut rect = Rectangle::new(pos, size);\n        clamp_preferring_top_left_in_area(self.working_area, &mut rect);\n        rect.loc\n    }\n\n    pub fn scale_by_working_area(&self, pos: Point<f64, SizeFrac>) -> Point<f64, Logical> {\n        Data::scale_by_working_area(self.working_area, pos)\n    }\n\n    pub fn logical_to_size_frac(&self, logical_pos: Point<f64, Logical>) -> Point<f64, SizeFrac> {\n        Data::logical_to_size_frac_in_working_area(self.working_area, logical_pos)\n    }\n\n    fn move_and_animate(&mut self, idx: usize, new_pos: Point<f64, Logical>) {\n        // Moves up to this logical pixel distance are not animated.\n        const ANIMATION_THRESHOLD_SQ: f64 = 10. * 10.;\n\n        let tile = &mut self.tiles[idx];\n        let data = &mut self.data[idx];\n\n        let prev_pos = data.logical_pos;\n        data.set_logical_pos(new_pos);\n        let new_pos = data.logical_pos;\n\n        let diff = prev_pos - new_pos;\n        if diff.x * diff.x + diff.y * diff.y > ANIMATION_THRESHOLD_SQ {\n            tile.animate_move_from(prev_pos - new_pos);\n        }\n    }\n\n    pub fn new_window_size(\n        &self,\n        width: Option<PresetSize>,\n        height: Option<PresetSize>,\n        rules: &ResolvedWindowRules,\n    ) -> Size<i32, Logical> {\n        let border = self.options.layout.border.merged_with(&rules.border);\n\n        let resolve = |size: Option<PresetSize>, working_area_size: f64| {\n            if let Some(size) = size {\n                let size = match resolve_preset_size(size, working_area_size) {\n                    ResolvedSize::Tile(mut size) => {\n                        if !border.off {\n                            size -= border.width * 2.;\n                        }\n                        size\n                    }\n                    ResolvedSize::Window(size) => size,\n                };\n\n                max(1, size.floor() as i32)\n            } else {\n                0\n            }\n        };\n\n        let width = resolve(width, self.working_area.size.w);\n        let height = resolve(height, self.working_area.size.h);\n\n        Size::from((width, height))\n    }\n\n    pub fn stored_or_default_tile_pos(&self, tile: &Tile<W>) -> Option<Point<f64, Logical>> {\n        let pos = tile.floating_pos.map(|pos| self.scale_by_working_area(pos));\n        pos.or_else(|| {\n            tile.window().rules().default_floating_position.map(|pos| {\n                let relative_to = pos.relative_to;\n                let size = tile.tile_size();\n                let area = self.working_area;\n\n                let mut pos = Point::from((pos.x.0, pos.y.0));\n                if relative_to == RelativeTo::TopRight\n                    || relative_to == RelativeTo::BottomRight\n                    || relative_to == RelativeTo::Right\n                {\n                    pos.x = area.size.w - size.w - pos.x;\n                }\n                if relative_to == RelativeTo::BottomLeft\n                    || relative_to == RelativeTo::BottomRight\n                    || relative_to == RelativeTo::Bottom\n                {\n                    pos.y = area.size.h - size.h - pos.y;\n                }\n                if relative_to == RelativeTo::Top || relative_to == RelativeTo::Bottom {\n                    pos.x += area.size.w / 2.0 - size.w / 2.0\n                }\n                if relative_to == RelativeTo::Left || relative_to == RelativeTo::Right {\n                    pos.y += area.size.h / 2.0 - size.h / 2.0\n                }\n\n                pos + self.working_area.loc\n            })\n        })\n    }\n\n    #[cfg(test)]\n    pub fn view_size(&self) -> Size<f64, Logical> {\n        self.view_size\n    }\n\n    pub fn working_area(&self) -> Rectangle<f64, Logical> {\n        self.working_area\n    }\n\n    #[cfg(test)]\n    pub fn scale(&self) -> f64 {\n        self.scale\n    }\n\n    #[cfg(test)]\n    pub fn clock(&self) -> &Clock {\n        &self.clock\n    }\n\n    #[cfg(test)]\n    pub fn options(&self) -> &Rc<Options> {\n        &self.options\n    }\n\n    #[cfg(test)]\n    pub fn verify_invariants(&self) {\n        assert!(self.scale > 0.);\n        assert!(self.scale.is_finite());\n        assert_eq!(self.tiles.len(), self.data.len());\n\n        for (i, (tile, data)) in zip(&self.tiles, &self.data).enumerate() {\n            use crate::layout::SizingMode;\n\n            assert!(Rc::ptr_eq(&self.options, &tile.options));\n            assert_eq!(self.view_size, tile.view_size());\n            assert_eq!(self.clock, tile.clock);\n            assert_eq!(self.scale, tile.scale());\n            tile.verify_invariants();\n\n            if let Some(idx) = tile.floating_preset_width_idx {\n                assert!(idx < self.options.layout.preset_column_widths.len());\n            }\n            if let Some(idx) = tile.floating_preset_height_idx {\n                assert!(idx < self.options.layout.preset_window_heights.len());\n            }\n\n            assert_eq!(\n                tile.window().pending_sizing_mode(),\n                SizingMode::Normal,\n                \"floating windows cannot be maximized or fullscreen\"\n            );\n\n            data.verify_invariants();\n\n            let mut data2 = *data;\n            data2.update(tile);\n            data2.update_config(self.working_area);\n            assert_eq!(data, &data2, \"tile data must be up to date\");\n\n            for tile_below in &self.tiles[i + 1..] {\n                assert!(\n                    !tile_below.window().is_child_of(tile.window()),\n                    \"children must be stacked above parents\"\n                );\n            }\n        }\n\n        if let Some(id) = &self.active_window_id {\n            assert!(!self.tiles.is_empty());\n            assert!(self.contains(id), \"active window must be present in tiles\");\n        } else {\n            assert!(self.tiles.is_empty());\n        }\n\n        if let Some(resize) = &self.interactive_resize {\n            assert!(\n                self.contains(&resize.window),\n                \"interactive resize window must be present in tiles\"\n            );\n        }\n    }\n}\n\nfn compute_toplevel_bounds(\n    border_config: niri_config::Border,\n    working_area_size: Size<f64, Logical>,\n) -> Size<i32, Logical> {\n    let mut border = 0.;\n    if !border_config.off {\n        border = border_config.width * 2.;\n    }\n\n    Size::from((\n        f64::max(working_area_size.w - border, 1.),\n        f64::max(working_area_size.h - border, 1.),\n    ))\n    .to_i32_floor()\n}\n\nfn resolve_preset_size(preset: PresetSize, view_size: f64) -> ResolvedSize {\n    match preset {\n        PresetSize::Proportion(proportion) => ResolvedSize::Tile(view_size * proportion),\n        PresetSize::Fixed(width) => ResolvedSize::Window(f64::from(width)),\n    }\n}\n"
  },
  {
    "path": "src/layout/focus_ring.rs",
    "content": "use std::iter::zip;\n\nuse niri_config::{CornerRadius, Gradient, GradientRelativeTo};\nuse smithay::backend::renderer::element::{Element as _, Kind};\nuse smithay::utils::{Logical, Point, Rectangle, Size};\n\nuse crate::niri_render_elements;\nuse crate::render_helpers::border::BorderRenderElement;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\n\n#[derive(Debug)]\npub struct FocusRing {\n    buffers: [SolidColorBuffer; 8],\n    locations: [Point<f64, Logical>; 8],\n    sizes: [Size<f64, Logical>; 8],\n    borders: [BorderRenderElement; 8],\n    full_size: Size<f64, Logical>,\n    is_border: bool,\n    use_border_shader: bool,\n    config: niri_config::FocusRing,\n    thicken_corners: bool,\n}\n\nniri_render_elements! {\n    FocusRingRenderElement => {\n        SolidColor = SolidColorRenderElement,\n        Gradient = BorderRenderElement,\n    }\n}\n\nimpl FocusRing {\n    pub fn new(config: niri_config::FocusRing) -> Self {\n        Self {\n            buffers: Default::default(),\n            locations: Default::default(),\n            sizes: Default::default(),\n            borders: Default::default(),\n            full_size: Default::default(),\n            is_border: false,\n            use_border_shader: false,\n            config,\n            thicken_corners: true,\n        }\n    }\n\n    pub fn update_config(&mut self, config: niri_config::FocusRing) {\n        self.config = config;\n    }\n\n    pub fn update_shaders(&mut self) {\n        for elem in &mut self.borders {\n            elem.damage_all();\n        }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn update_render_elements(\n        &mut self,\n        win_size: Size<f64, Logical>,\n        is_active: bool,\n        is_border: bool,\n        is_urgent: bool,\n        view_rect: Rectangle<f64, Logical>,\n        radius: CornerRadius,\n        scale: f64,\n        alpha: f32,\n    ) {\n        let width = self.config.width;\n        self.full_size = win_size + Size::from((width, width)).upscale(2.);\n        self.is_border = is_border;\n\n        let color = if is_urgent {\n            self.config.urgent_color\n        } else if is_active {\n            self.config.active_color\n        } else {\n            self.config.inactive_color\n        };\n\n        for buf in &mut self.buffers {\n            buf.set_color(color);\n        }\n\n        let radius = radius.fit_to(self.full_size.w as f32, self.full_size.h as f32);\n\n        let gradient = if is_urgent {\n            self.config.urgent_gradient\n        } else if is_active {\n            self.config.active_gradient\n        } else {\n            self.config.inactive_gradient\n        };\n\n        self.use_border_shader = radius != CornerRadius::default() || gradient.is_some();\n\n        // Set the defaults for solid color + rounded corners.\n        let gradient = gradient.unwrap_or_else(|| Gradient::from(color));\n\n        let full_rect = Rectangle::new(Point::from((-width, -width)), self.full_size);\n        let gradient_area = match gradient.relative_to {\n            GradientRelativeTo::Window => full_rect,\n            GradientRelativeTo::WorkspaceView => view_rect,\n        };\n\n        let rounded_corner_border_width = if is_border {\n            // HACK: increase the border width used for the inner rounded corners a tiny bit to\n            // reduce background bleed.\n            let extra = if self.thicken_corners { 0.5 } else { 0. };\n            width as f32 + extra\n        } else {\n            0.\n        };\n\n        let ceil = |logical: f64| (logical * scale).ceil() / scale;\n\n        // All of this stuff should end up aligned to physical pixels because:\n        // * Window size and border width are rounded to physical pixels before being passed to this\n        //   function.\n        // * We will ceil the corner radii below.\n        // * We do not divide anything, only add, subtract and multiply by integers.\n        // * At rendering time, tile positions are rounded to physical pixels.\n\n        if is_border {\n            let top_left = f64::max(width, ceil(f64::from(radius.top_left)));\n            let top_right = f64::min(\n                self.full_size.w - top_left,\n                f64::max(width, ceil(f64::from(radius.top_right))),\n            );\n            let bottom_left = f64::min(\n                self.full_size.h - top_left,\n                f64::max(width, ceil(f64::from(radius.bottom_left))),\n            );\n            let bottom_right = f64::min(\n                self.full_size.h - top_right,\n                f64::min(\n                    self.full_size.w - bottom_left,\n                    f64::max(width, ceil(f64::from(radius.bottom_right))),\n                ),\n            );\n\n            // Top edge.\n            self.sizes[0] = Size::from((win_size.w + width * 2. - top_left - top_right, width));\n            self.locations[0] = Point::from((-width + top_left, -width));\n\n            // Bottom edge.\n            self.sizes[1] =\n                Size::from((win_size.w + width * 2. - bottom_left - bottom_right, width));\n            self.locations[1] = Point::from((-width + bottom_left, win_size.h));\n\n            // Left edge.\n            self.sizes[2] = Size::from((width, win_size.h + width * 2. - top_left - bottom_left));\n            self.locations[2] = Point::from((-width, -width + top_left));\n\n            // Right edge.\n            self.sizes[3] = Size::from((width, win_size.h + width * 2. - top_right - bottom_right));\n            self.locations[3] = Point::from((win_size.w, -width + top_right));\n\n            // Top-left corner.\n            self.sizes[4] = Size::from((top_left, top_left));\n            self.locations[4] = Point::from((-width, -width));\n\n            // Top-right corner.\n            self.sizes[5] = Size::from((top_right, top_right));\n            self.locations[5] = Point::from((win_size.w + width - top_right, -width));\n\n            // Bottom-right corner.\n            self.sizes[6] = Size::from((bottom_right, bottom_right));\n            self.locations[6] = Point::from((\n                win_size.w + width - bottom_right,\n                win_size.h + width - bottom_right,\n            ));\n\n            // Bottom-left corner.\n            self.sizes[7] = Size::from((bottom_left, bottom_left));\n            self.locations[7] = Point::from((-width, win_size.h + width - bottom_left));\n\n            for (buf, size) in zip(&mut self.buffers, self.sizes) {\n                buf.resize(size);\n            }\n\n            for (border, (loc, size)) in zip(&mut self.borders, zip(self.locations, self.sizes)) {\n                border.update(\n                    size,\n                    Rectangle::new(gradient_area.loc - loc, gradient_area.size),\n                    gradient.in_,\n                    gradient.from,\n                    gradient.to,\n                    ((gradient.angle as f32) - 90.).to_radians(),\n                    Rectangle::new(full_rect.loc - loc, full_rect.size),\n                    rounded_corner_border_width,\n                    radius,\n                    scale as f32,\n                    alpha,\n                );\n            }\n        } else {\n            self.sizes[0] = self.full_size;\n            self.buffers[0].resize(self.sizes[0]);\n            self.locations[0] = Point::from((-width, -width));\n\n            self.borders[0].update(\n                self.sizes[0],\n                Rectangle::new(gradient_area.loc - self.locations[0], gradient_area.size),\n                gradient.in_,\n                gradient.from,\n                gradient.to,\n                ((gradient.angle as f32) - 90.).to_radians(),\n                Rectangle::new(full_rect.loc - self.locations[0], full_rect.size),\n                rounded_corner_border_width,\n                radius,\n                scale as f32,\n                alpha,\n            );\n        }\n    }\n\n    pub fn render(\n        &self,\n        renderer: &mut impl NiriRenderer,\n        location: Point<f64, Logical>,\n        push: &mut dyn FnMut(FocusRingRenderElement),\n    ) {\n        if self.config.off {\n            return;\n        }\n\n        let border_width = -self.locations[0].y;\n\n        // If drawing as a border with width = 0, then there's nothing to draw.\n        if self.is_border && border_width == 0. {\n            return;\n        }\n\n        let has_border_shader = BorderRenderElement::has_shader(renderer);\n\n        let mut push = |buffer, border: &BorderRenderElement, location: Point<f64, Logical>| {\n            let elem = if self.use_border_shader && has_border_shader {\n                border.clone().with_location(location).into()\n            } else {\n                let alpha = border.alpha();\n                SolidColorRenderElement::from_buffer(buffer, location, alpha, Kind::Unspecified)\n                    .into()\n            };\n            push(elem);\n        };\n\n        if self.is_border {\n            for ((buf, border), loc) in zip(zip(&self.buffers, &self.borders), self.locations) {\n                push(buf, border, location + loc);\n            }\n        } else {\n            push(\n                &self.buffers[0],\n                &self.borders[0],\n                location + self.locations[0],\n            );\n        }\n    }\n\n    pub fn width(&self) -> f64 {\n        self.config.width\n    }\n\n    pub fn is_off(&self) -> bool {\n        self.config.off\n    }\n\n    pub fn set_thicken_corners(&mut self, value: bool) {\n        self.thicken_corners = value;\n    }\n\n    pub fn config(&self) -> &niri_config::FocusRing {\n        &self.config\n    }\n}\n"
  },
  {
    "path": "src/layout/insert_hint_element.rs",
    "content": "use niri_config::CornerRadius;\nuse smithay::utils::{Logical, Point, Rectangle, Size};\n\nuse super::focus_ring::{FocusRing, FocusRingRenderElement};\nuse crate::render_helpers::renderer::NiriRenderer;\n\n#[derive(Debug)]\npub struct InsertHintElement {\n    inner: FocusRing,\n}\n\npub type InsertHintRenderElement = FocusRingRenderElement;\n\nimpl InsertHintElement {\n    pub fn new(config: niri_config::InsertHint) -> Self {\n        Self {\n            inner: FocusRing::new(niri_config::FocusRing {\n                off: config.off,\n                width: 0.,\n                active_color: config.color,\n                inactive_color: config.color,\n                urgent_color: config.color,\n                active_gradient: config.gradient,\n                inactive_gradient: config.gradient,\n                urgent_gradient: config.gradient,\n            }),\n        }\n    }\n\n    pub fn update_config(&mut self, config: niri_config::InsertHint) {\n        self.inner.update_config(niri_config::FocusRing {\n            off: config.off,\n            width: 0.,\n            active_color: config.color,\n            inactive_color: config.color,\n            urgent_color: config.color,\n            active_gradient: config.gradient,\n            inactive_gradient: config.gradient,\n            urgent_gradient: config.gradient,\n        });\n    }\n\n    pub fn update_shaders(&mut self) {\n        self.inner.update_shaders();\n    }\n\n    pub fn update_render_elements(\n        &mut self,\n        size: Size<f64, Logical>,\n        view_rect: Rectangle<f64, Logical>,\n        radius: CornerRadius,\n        scale: f64,\n    ) {\n        self.inner\n            .update_render_elements(size, true, false, false, view_rect, radius, scale, 1.);\n    }\n\n    pub fn render(\n        &self,\n        renderer: &mut impl NiriRenderer,\n        location: Point<f64, Logical>,\n        push: &mut dyn FnMut(FocusRingRenderElement),\n    ) {\n        self.inner.render(renderer, location, push)\n    }\n}\n"
  },
  {
    "path": "src/layout/mod.rs",
    "content": "//! Window layout logic.\n//!\n//! Niri implements scrollable tiling with dynamic workspaces. The scrollable tiling is mostly\n//! orthogonal to any particular workspace system, though outputs living in separate coordinate\n//! spaces suggest per-output workspaces.\n//!\n//! I chose a dynamic workspace system because I think it works very well. In particular, it works\n//! naturally across outputs getting added and removed, since workspaces can move between outputs\n//! as necessary.\n//!\n//! In the layout, one output (the first one to be added) is designated as *primary*. This is where\n//! workspaces from disconnected outputs will move. Currently, the primary output has no other\n//! distinction from other outputs.\n//!\n//! Where possible, niri tries to follow these principles with regards to outputs:\n//!\n//! 1. Disconnecting and reconnecting the same output must not change the layout.\n//!    * This includes both secondary outputs and the primary output.\n//! 2. Connecting an output must not change the layout for any workspaces that were never on that\n//!    output.\n//!\n//! Therefore, we implement the following logic: every workspace keeps track of which output it\n//! originated on—its *original output*. When an output disconnects, its workspaces are appended to\n//! the (potentially new) primary output, but remember their original output. Then, if the original\n//! output connects again, all workspaces originally from there move back to that output.\n//!\n//! In order to avoid surprising behavior, if the user creates or moves any new windows onto a\n//! workspace, it forgets its original output, and its current output becomes its original output.\n//! Imagine a scenario: the user works with a laptop and a monitor at home, then takes their laptop\n//! with them, disconnecting the monitor, and keeps working as normal, using the second monitor's\n//! workspace just like any other. Then they come back, reconnect the second monitor, and now we\n//! don't want an unassuming workspace to end up on it.\n\nuse std::collections::HashMap;\nuse std::mem;\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse monitor::{InsertHint, InsertPosition, InsertWorkspace, MonitorAddWindowTarget};\nuse niri_config::utils::MergeWith as _;\nuse niri_config::{\n    Config, CornerRadius, LayoutPart, PresetSize, Workspace as WorkspaceConfig, WorkspaceReference,\n};\nuse niri_ipc::{ColumnDisplay, PositionChange, SizeChange, WindowLayout};\nuse scrolling::{Column, ColumnWidth};\nuse smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;\nuse smithay::backend::renderer::element::utils::RescaleRenderElement;\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\nuse smithay::output::{self, Output};\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size, Transform};\nuse tile::{Tile, TileRenderElement};\nuse workspace::{WorkspaceAddWindowTarget, WorkspaceId};\n\npub use self::monitor::MonitorRenderElement;\nuse self::monitor::{Monitor, WorkspaceSwitch};\nuse self::workspace::{OutputId, Workspace};\nuse crate::animation::{Animation, Clock};\nuse crate::input::swipe_tracker::SwipeTracker;\nuse crate::layout::scrolling::ScrollDirection;\nuse crate::niri_render_elements;\nuse crate::render_helpers::offscreen::OffscreenData;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::snapshot::RenderSnapshot;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::texture::TextureBuffer;\nuse crate::render_helpers::{BakedBuffer, RenderTarget};\nuse crate::rubber_band::RubberBand;\nuse crate::utils::transaction::{Transaction, TransactionBlocker};\nuse crate::utils::{\n    ensure_min_max_size_maybe_zero, output_matches_name, output_size,\n    round_logical_in_physical_max1, ResizeEdge,\n};\nuse crate::window::ResolvedWindowRules;\n\npub mod closing_window;\npub mod floating;\npub mod focus_ring;\npub mod insert_hint_element;\npub mod monitor;\npub mod opening_window;\npub mod scrolling;\npub mod shadow;\npub mod tab_indicator;\npub mod tile;\npub mod workspace;\n\n#[cfg(test)]\nmod tests;\n\n/// Size changes up to this many pixels don't animate.\npub const RESIZE_ANIMATION_THRESHOLD: f64 = 10.;\n\n/// Pointer needs to move this far to pull a window from the layout.\nconst INTERACTIVE_MOVE_START_THRESHOLD: f64 = 256. * 256.;\n\n/// Opacity of interactively moved tiles targeting the scrolling layout.\nconst INTERACTIVE_MOVE_ALPHA: f64 = 0.75;\n\n/// Amount of touchpad movement to toggle the overview.\nconst OVERVIEW_GESTURE_MOVEMENT: f64 = 300.;\n\nconst OVERVIEW_GESTURE_RUBBER_BAND: RubberBand = RubberBand {\n    stiffness: 0.5,\n    limit: 0.05,\n};\n\n/// Size-relative units.\npub struct SizeFrac;\n\nniri_render_elements! {\n    LayoutElementRenderElement<R> => {\n        Wayland = WaylandSurfaceRenderElement<R>,\n        SolidColor = SolidColorRenderElement,\n    }\n}\n\npub type LayoutElementRenderSnapshot =\n    RenderSnapshot<BakedBuffer<TextureBuffer<GlesTexture>>, BakedBuffer<SolidColorBuffer>>;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum SizingMode {\n    Normal,\n    Maximized,\n    Fullscreen,\n}\n\npub trait LayoutElement {\n    /// Type that can be used as a unique ID of this element.\n    type Id: PartialEq + std::fmt::Debug + Clone;\n\n    /// Unique ID of this element.\n    fn id(&self) -> &Self::Id;\n\n    /// Visual size of the element.\n    ///\n    /// This is what the user would consider the size, i.e. excluding CSD shadows and whatnot.\n    /// Corresponds to the Wayland window geometry size.\n    fn size(&self) -> Size<i32, Logical>;\n\n    /// Returns the location of the element's buffer relative to the element's visual geometry.\n    ///\n    /// I.e. if the element has CSD shadows, its buffer location will have negative coordinates.\n    fn buf_loc(&self) -> Point<i32, Logical>;\n\n    /// Checks whether a point is in the element's input region.\n    ///\n    /// The point is relative to the element's visual geometry.\n    fn is_in_input_region(&self, point: Point<f64, Logical>) -> bool;\n\n    /// Renders the element at the given visual location.\n    ///\n    /// The element should be rendered in such a way that its visual geometry ends up at the given\n    /// location.\n    fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        scale: Scale<f64>,\n        alpha: f32,\n        target: RenderTarget,\n        push: &mut dyn FnMut(LayoutElementRenderElement<R>),\n    ) {\n        self.render_popups(renderer, location, scale, alpha, target, push);\n        self.render_normal(renderer, location, scale, alpha, target, push);\n    }\n\n    /// Renders the non-popup parts of the element.\n    fn render_normal<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        scale: Scale<f64>,\n        alpha: f32,\n        target: RenderTarget,\n        push: &mut dyn FnMut(LayoutElementRenderElement<R>),\n    ) {\n        let _ = (renderer, location, scale, alpha, target, push);\n    }\n\n    /// Renders the popups of the element.\n    fn render_popups<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        scale: Scale<f64>,\n        alpha: f32,\n        target: RenderTarget,\n        push: &mut dyn FnMut(LayoutElementRenderElement<R>),\n    ) {\n        let _ = (renderer, location, scale, alpha, target, push);\n    }\n\n    /// Requests the element to change its size.\n    ///\n    /// The size request is stored and will be continuously sent to the element on any further\n    /// state changes.\n    fn request_size(\n        &mut self,\n        size: Size<i32, Logical>,\n        mode: SizingMode,\n        animate: bool,\n        transaction: Option<Transaction>,\n    );\n\n    /// Requests the element to change size once, clearing the request afterwards.\n    fn request_size_once(&mut self, size: Size<i32, Logical>, animate: bool) {\n        self.request_size(size, SizingMode::Normal, animate, None);\n    }\n\n    fn min_size(&self) -> Size<i32, Logical>;\n    fn max_size(&self) -> Size<i32, Logical>;\n    fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool;\n    fn has_ssd(&self) -> bool;\n    fn set_preferred_scale_transform(&self, scale: output::Scale, transform: Transform);\n    fn output_enter(&self, output: &Output);\n    fn output_leave(&self, output: &Output);\n    fn set_offscreen_data(&self, data: Option<OffscreenData>);\n    fn set_activated(&mut self, active: bool);\n    fn set_active_in_column(&mut self, active: bool);\n    fn set_floating(&mut self, floating: bool);\n    fn set_bounds(&self, bounds: Size<i32, Logical>);\n    fn is_ignoring_opacity_window_rule(&self) -> bool;\n\n    fn is_urgent(&self) -> bool;\n\n    fn configure_intent(&self) -> ConfigureIntent;\n    fn send_pending_configure(&mut self);\n\n    /// The element's current sizing mode.\n    ///\n    /// This will *not* switch immediately after a [`LayoutElement::request_size()`] call.\n    fn sizing_mode(&self) -> SizingMode;\n\n    /// The sizing mode that we're requesting the element to assume.\n    ///\n    /// This *will* switch immediately after a [`LayoutElement::request_size()`] call.\n    fn pending_sizing_mode(&self) -> SizingMode;\n\n    /// Size previously requested through [`LayoutElement::request_size()`].\n    fn requested_size(&self) -> Option<Size<i32, Logical>>;\n\n    /// Non-fullscreen size that we expect this window has or will shortly have.\n    ///\n    /// This can be different from [`requested_size()`](LayoutElement::requested_size()). For\n    /// example, for floating windows this will generally return the current window size, rather\n    /// than the last size that we requested, since we want floating windows to be able to change\n    /// size freely. But not always: if we just requested a floating window to resize and it hasn't\n    /// responded to it yet, this will return the newly requested size.\n    ///\n    /// This function should never return a 0 size component. `None` means there's no known\n    /// expected size (for example, the window is fullscreen).\n    ///\n    /// The default impl is for testing only, it will not preserve the window's own size changes.\n    fn expected_size(&self) -> Option<Size<i32, Logical>> {\n        if self.sizing_mode().is_fullscreen() {\n            return None;\n        }\n\n        let mut requested = self.requested_size().unwrap_or_default();\n        let current = self.size();\n        if requested.w == 0 {\n            requested.w = current.w;\n        }\n        if requested.h == 0 {\n            requested.h = current.h;\n        }\n        Some(requested)\n    }\n\n    fn is_pending_windowed_fullscreen(&self) -> bool {\n        false\n    }\n    fn request_windowed_fullscreen(&mut self, value: bool) {\n        let _ = value;\n    }\n\n    fn is_child_of(&self, parent: &Self) -> bool;\n\n    fn rules(&self) -> &ResolvedWindowRules;\n\n    /// Runs periodic clean-up tasks.\n    fn refresh(&self);\n\n    fn take_animation_snapshot(&mut self) -> Option<LayoutElementRenderSnapshot>;\n\n    fn set_interactive_resize(&mut self, data: Option<InteractiveResizeData>);\n    fn cancel_interactive_resize(&mut self);\n    fn interactive_resize_data(&self) -> Option<InteractiveResizeData>;\n\n    fn on_commit(&mut self, serial: Serial);\n}\n\n#[derive(Debug)]\npub struct Layout<W: LayoutElement> {\n    /// Monitors and workspaes in the layout.\n    monitor_set: MonitorSet<W>,\n    /// Whether the layout should draw as active.\n    ///\n    /// This normally indicates that the layout has keyboard focus, but not always. E.g. when the\n    /// screenshot UI is open, it keeps the layout drawing as active.\n    is_active: bool,\n    /// Map from monitor name to id of its last active workspace.\n    ///\n    /// This data is stored upon monitor removal and is used to restore the active workspace when\n    /// the monitor is reconnected.\n    ///\n    /// The workspace id does not necessarily point to a valid workspace. If it doesn't, then it is\n    /// simply ignored.\n    last_active_workspace_id: HashMap<String, WorkspaceId>,\n    /// Ongoing interactive move.\n    interactive_move: Option<InteractiveMoveState<W>>,\n    /// Ongoing drag-and-drop operation.\n    dnd: Option<DndData<W>>,\n    /// Clock for driving animations.\n    clock: Clock,\n    /// Time that we last updated render elements for.\n    update_render_elements_time: Duration,\n    /// Whether the overview is open.\n    ///\n    /// This is a boolean flag that controls things like where input goes to. The actual animation\n    /// is controlled by overview_progress.\n    overview_open: bool,\n    /// The overview zoom progress.\n    overview_progress: Option<OverviewProgress>,\n    /// Configurable properties of the layout.\n    options: Rc<Options>,\n}\n\n#[derive(Debug)]\nenum MonitorSet<W: LayoutElement> {\n    /// At least one output is connected.\n    Normal {\n        /// Connected monitors.\n        monitors: Vec<Monitor<W>>,\n        /// Index of the primary monitor.\n        primary_idx: usize,\n        /// Index of the active monitor.\n        active_monitor_idx: usize,\n    },\n    /// No outputs are connected, and these are the workspaces.\n    NoOutputs {\n        /// The workspaces.\n        workspaces: Vec<Workspace<W>>,\n    },\n}\n\n#[derive(Debug, Default, Clone, PartialEq)]\npub struct Options {\n    pub layout: niri_config::Layout,\n    pub animations: niri_config::Animations,\n    pub gestures: niri_config::Gestures,\n    pub overview: niri_config::Overview,\n    // Debug flags.\n    pub disable_resize_throttling: bool,\n    pub disable_transactions: bool,\n    pub deactivate_unfocused_windows: bool,\n}\n\n#[allow(clippy::large_enum_variant)]\n#[derive(Debug)]\nenum InteractiveMoveState<W: LayoutElement> {\n    /// Initial rubberbanding; the window remains in the layout.\n    Starting {\n        /// The window we're moving.\n        window_id: W::Id,\n        /// Current pointer delta from the starting location.\n        pointer_delta: Point<f64, Logical>,\n        /// Pointer location within the visual window geometry as ratio from geometry size.\n        ///\n        /// This helps the pointer remain inside the window as it resizes.\n        pointer_ratio_within_window: (f64, f64),\n    },\n    /// Moving; the window is no longer in the layout.\n    Moving(InteractiveMoveData<W>),\n}\n\n#[derive(Debug)]\nstruct InteractiveMoveData<W: LayoutElement> {\n    /// The window being moved.\n    pub(self) tile: Tile<W>,\n    /// Output where the window is currently located/rendered.\n    pub(self) output: Output,\n    /// Current pointer position within output.\n    pub(self) pointer_pos_within_output: Point<f64, Logical>,\n    /// Window column width.\n    pub(self) width: ColumnWidth,\n    /// Whether the window column was full-width.\n    pub(self) is_full_width: bool,\n    /// Whether the window targets the floating layout.\n    pub(self) is_floating: bool,\n    /// Pointer location within the visual window geometry as ratio from geometry size.\n    ///\n    /// This helps the pointer remain inside the window as it resizes.\n    pub(self) pointer_ratio_within_window: (f64, f64),\n    /// Config overrides for the output where the window is currently located.\n    ///\n    /// Cached here to be accessible while an output is removed.\n    pub(self) output_config: Option<niri_config::LayoutPart>,\n    /// Config overrides for the workspace where the window is currently located.\n    ///\n    /// To avoid sudden window changes when starting an interactive move, it will remember the\n    /// config overrides for the workspace where the move originated from. As soon as the window\n    /// moves over some different workspace though, this override will reset.\n    pub(self) workspace_config: Option<(WorkspaceId, niri_config::LayoutPart)>,\n}\n\n#[derive(Debug)]\npub struct DndData<W: LayoutElement> {\n    /// Output where the pointer is currently located.\n    output: Output,\n    /// Current pointer position within output.\n    pointer_pos_within_output: Point<f64, Logical>,\n    /// Ongoing DnD hold to activate something.\n    hold: Option<DndHold<W>>,\n}\n\n#[derive(Debug)]\nstruct DndHold<W: LayoutElement> {\n    /// Time when we started holding on the target.\n    start_time: Duration,\n    target: DndHoldTarget<W::Id>,\n}\n\n#[derive(Debug, PartialEq, Eq)]\nenum DndHoldTarget<WindowId> {\n    Window(WindowId),\n    Workspace(WorkspaceId),\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct InteractiveResizeData {\n    pub(self) edges: ResizeEdge,\n}\n\n#[derive(Debug, Clone, Copy)]\npub enum ConfigureIntent {\n    /// A configure is not needed (no changes to server pending state).\n    NotNeeded,\n    /// A configure is throttled (due to resizing too fast for example).\n    Throttled,\n    /// Can send the configure if it isn't throttled externally (only size changed).\n    CanSend,\n    /// Should send the configure regardless of external throttling (something other than size\n    /// changed).\n    ShouldSend,\n}\n\n/// Tile that was just removed from the layout.\npub struct RemovedTile<W: LayoutElement> {\n    tile: Tile<W>,\n    /// Width of the column the tile was in.\n    width: ColumnWidth,\n    /// Whether the column the tile was in was full-width.\n    is_full_width: bool,\n    /// Whether the tile was floating.\n    is_floating: bool,\n}\n\n/// Whether to activate a newly added window.\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum ActivateWindow {\n    /// Activate unconditionally.\n    Yes,\n    /// Activate based on heuristics.\n    #[default]\n    Smart,\n    /// Do not activate.\n    No,\n}\n\n/// Where to put a newly added window.\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum AddWindowTarget<'a, W: LayoutElement> {\n    /// No particular preference.\n    #[default]\n    Auto,\n    /// On this output.\n    Output(&'a Output),\n    /// On this workspace.\n    Workspace(WorkspaceId),\n    /// Next to this existing window.\n    NextTo(&'a W::Id),\n}\n\n/// Type of the window hit from `window_under()`.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum HitType {\n    /// The hit is within a window's input region and can be used for sending events to it.\n    Input {\n        /// Position of the window's buffer.\n        win_pos: Point<f64, Logical>,\n    },\n    /// The hit can activate a window, but it is not in the input region so cannot send events.\n    ///\n    /// For example, this could be clicking on a tile border outside the window.\n    Activate {\n        /// Whether the hit was on the tab indicator.\n        is_tab_indicator: bool,\n    },\n}\n\n#[derive(Debug)]\nenum OverviewProgress {\n    Animation(Animation),\n    Gesture(OverviewGesture),\n    Open,\n}\n\n#[derive(Debug)]\nstruct OverviewGesture {\n    tracker: SwipeTracker,\n    /// Start point.\n    start: f64,\n    /// Current progress.\n    value: f64,\n}\n\nimpl SizingMode {\n    #[must_use]\n    pub fn is_normal(&self) -> bool {\n        matches!(self, Self::Normal)\n    }\n\n    #[must_use]\n    pub fn is_fullscreen(&self) -> bool {\n        matches!(self, Self::Fullscreen)\n    }\n\n    #[must_use]\n    pub fn is_maximized(&self) -> bool {\n        matches!(self, Self::Maximized)\n    }\n}\n\nimpl<W: LayoutElement> InteractiveMoveState<W> {\n    fn moving(&self) -> Option<&InteractiveMoveData<W>> {\n        match self {\n            InteractiveMoveState::Moving(move_) => Some(move_),\n            _ => None,\n        }\n    }\n\n    fn moving_mut(&mut self) -> Option<&mut InteractiveMoveData<W>> {\n        match self {\n            InteractiveMoveState::Moving(move_) => Some(move_),\n            _ => None,\n        }\n    }\n}\n\nimpl<W: LayoutElement> InteractiveMoveData<W> {\n    fn tile_render_location(&self, zoom: f64) -> Point<f64, Logical> {\n        let scale = Scale::from(self.output.current_scale().fractional_scale());\n        let window_size = self.tile.window_size();\n        let pointer_offset_within_window = Point::from((\n            window_size.w * self.pointer_ratio_within_window.0,\n            window_size.h * self.pointer_ratio_within_window.1,\n        ));\n        let pos = self.pointer_pos_within_output\n            - (pointer_offset_within_window + self.tile.window_loc() - self.tile.render_offset())\n                .upscale(zoom);\n        // Round to physical pixels.\n        pos.to_physical_precise_round(scale).to_logical(scale)\n    }\n}\n\nimpl ActivateWindow {\n    pub fn map_smart(self, f: impl FnOnce() -> bool) -> bool {\n        match self {\n            ActivateWindow::Yes => true,\n            ActivateWindow::Smart => f(),\n            ActivateWindow::No => false,\n        }\n    }\n}\n\nimpl HitType {\n    pub fn offset_win_pos(mut self, offset: Point<f64, Logical>) -> Self {\n        match &mut self {\n            HitType::Input { win_pos } => *win_pos += offset,\n            HitType::Activate { .. } => (),\n        }\n        self\n    }\n\n    pub fn hit_tile<W: LayoutElement>(\n        tile: &Tile<W>,\n        tile_pos: Point<f64, Logical>,\n        point: Point<f64, Logical>,\n    ) -> Option<(&W, Self)> {\n        let pos_within_tile = point - tile_pos;\n        tile.hit(pos_within_tile)\n            .map(|hit| (tile.window(), hit.offset_win_pos(tile_pos)))\n    }\n\n    pub fn to_activate(self) -> Self {\n        match self {\n            HitType::Input { .. } => HitType::Activate {\n                is_tab_indicator: false,\n            },\n            HitType::Activate { .. } => self,\n        }\n    }\n}\n\nimpl Options {\n    fn from_config(config: &Config) -> Self {\n        Self {\n            layout: config.layout.clone(),\n            animations: config.animations.clone(),\n            gestures: config.gestures,\n            overview: config.overview,\n            disable_resize_throttling: config.debug.disable_resize_throttling,\n            disable_transactions: config.debug.disable_transactions,\n            deactivate_unfocused_windows: config.debug.deactivate_unfocused_windows,\n        }\n    }\n\n    fn with_merged_layout(mut self, part: Option<&niri_config::LayoutPart>) -> Self {\n        if let Some(part) = part {\n            self.layout.merge_with(part);\n        }\n        self\n    }\n\n    fn adjusted_for_scale(mut self, scale: f64) -> Self {\n        self.layout.gaps = round_logical_in_physical_max1(scale, self.layout.gaps);\n        self\n    }\n}\n\nimpl OverviewProgress {\n    fn value(&self) -> f64 {\n        match self {\n            OverviewProgress::Animation(anim) => anim.value(),\n            OverviewProgress::Gesture(gesture) => gesture.value,\n            OverviewProgress::Open => 1.,\n        }\n    }\n\n    fn is_animation(&self) -> bool {\n        matches!(self, OverviewProgress::Animation(_))\n    }\n}\n\nimpl<W: LayoutElement> Layout<W> {\n    pub fn new(clock: Clock, config: &Config) -> Self {\n        Self::with_options_and_workspaces(clock, config, Options::from_config(config))\n    }\n\n    pub fn with_options(clock: Clock, options: Options) -> Self {\n        Self {\n            monitor_set: MonitorSet::NoOutputs { workspaces: vec![] },\n            is_active: true,\n            last_active_workspace_id: HashMap::new(),\n            interactive_move: None,\n            dnd: None,\n            clock,\n            update_render_elements_time: Duration::ZERO,\n            overview_open: false,\n            overview_progress: None,\n            options: Rc::new(options),\n        }\n    }\n\n    fn with_options_and_workspaces(clock: Clock, config: &Config, options: Options) -> Self {\n        let opts = Rc::new(options);\n\n        let workspaces = config\n            .workspaces\n            .iter()\n            .map(|ws| {\n                Workspace::new_with_config_no_outputs(Some(ws.clone()), clock.clone(), opts.clone())\n            })\n            .collect();\n\n        Self {\n            monitor_set: MonitorSet::NoOutputs { workspaces },\n            is_active: true,\n            last_active_workspace_id: HashMap::new(),\n            interactive_move: None,\n            dnd: None,\n            clock,\n            update_render_elements_time: Duration::ZERO,\n            overview_open: false,\n            overview_progress: None,\n            options: opts,\n        }\n    }\n\n    pub fn add_output(&mut self, output: Output, layout_config: Option<LayoutPart>) {\n        self.monitor_set = match mem::take(&mut self.monitor_set) {\n            MonitorSet::Normal {\n                mut monitors,\n                primary_idx,\n                active_monitor_idx,\n            } => {\n                let primary = &mut monitors[primary_idx];\n\n                let mut stopped_primary_ws_switch = false;\n\n                let mut workspaces = vec![];\n                for i in (0..primary.workspaces.len()).rev() {\n                    if primary.workspaces[i].original_output.matches(&output) {\n                        let ws = primary.workspaces.remove(i);\n\n                        // FIXME: this can be coded in a way that the workspace switch won't be\n                        // affected if the removed workspace is invisible. But this is good enough\n                        // for now.\n                        if primary.workspace_switch.is_some() {\n                            primary.workspace_switch = None;\n                            stopped_primary_ws_switch = true;\n                        }\n\n                        // The user could've closed a window while remaining on this workspace, on\n                        // another monitor. However, we will add an empty workspace in the end\n                        // instead.\n                        if ws.has_windows_or_name() {\n                            workspaces.push(ws);\n                        }\n\n                        if i <= primary.active_workspace_idx\n                            // Generally when moving the currently active workspace, we want to\n                            // fall back to the workspace above, so as not to end up on the last\n                            // empty workspace. However, with empty workspace above first, when\n                            // moving the workspace at index 1 (first non-empty), we want to stay\n                            // at index 1, so as once again not to end up on an empty workspace.\n                            //\n                            // This comes into play at compositor startup when having named\n                            // workspaces set up across multiple monitors. Without this check, the\n                            // first monitor to connect can end up with the first empty workspace\n                            // focused instead of the first named workspace.\n                            && !(primary.options.layout.empty_workspace_above_first\n                                && primary.active_workspace_idx == 1)\n                        {\n                            primary.active_workspace_idx =\n                                primary.active_workspace_idx.saturating_sub(1);\n                        }\n                    }\n                }\n\n                // If we stopped a workspace switch, then we might need to clean up workspaces.\n                // Also if empty_workspace_above_first is set and there are only 2 workspaces left,\n                // both will be empty and one of them needs to be removed. clean_up_workspaces\n                // takes care of this.\n\n                if stopped_primary_ws_switch\n                    || (primary.options.layout.empty_workspace_above_first\n                        && primary.workspaces.len() == 2)\n                {\n                    primary.clean_up_workspaces();\n                }\n\n                workspaces.reverse();\n\n                let ws_id_to_activate = self.last_active_workspace_id.remove(&output.name());\n\n                let mut monitor = Monitor::new(\n                    output,\n                    workspaces,\n                    ws_id_to_activate,\n                    self.clock.clone(),\n                    self.options.clone(),\n                    layout_config,\n                );\n                monitor.overview_open = self.overview_open;\n                monitor.set_overview_progress(self.overview_progress.as_ref());\n                monitors.push(monitor);\n\n                MonitorSet::Normal {\n                    monitors,\n                    primary_idx,\n                    active_monitor_idx,\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                let ws_id_to_activate = self.last_active_workspace_id.remove(&output.name());\n\n                let mut monitor = Monitor::new(\n                    output,\n                    workspaces,\n                    ws_id_to_activate,\n                    self.clock.clone(),\n                    self.options.clone(),\n                    layout_config,\n                );\n                monitor.overview_open = self.overview_open;\n                monitor.set_overview_progress(self.overview_progress.as_ref());\n\n                MonitorSet::Normal {\n                    monitors: vec![monitor],\n                    primary_idx: 0,\n                    active_monitor_idx: 0,\n                }\n            }\n        }\n    }\n\n    pub fn remove_output(&mut self, output: &Output) {\n        self.monitor_set = match mem::take(&mut self.monitor_set) {\n            MonitorSet::Normal {\n                mut monitors,\n                mut primary_idx,\n                mut active_monitor_idx,\n            } => {\n                let idx = monitors\n                    .iter()\n                    .position(|mon| &mon.output == output)\n                    .expect(\"trying to remove non-existing output\");\n                let monitor = monitors.remove(idx);\n\n                self.last_active_workspace_id.insert(\n                    monitor.output_name().clone(),\n                    monitor.workspaces[monitor.active_workspace_idx].id(),\n                );\n\n                let mut workspaces = monitor.into_workspaces();\n\n                if monitors.is_empty() {\n                    // Removed the last monitor.\n\n                    for ws in &mut workspaces {\n                        // Reset base options to layout ones.\n                        ws.update_config(self.options.clone());\n                    }\n\n                    MonitorSet::NoOutputs { workspaces }\n                } else {\n                    if primary_idx >= idx {\n                        // Update primary_idx to either still point at the same monitor, or at some\n                        // other monitor if the primary has been removed.\n                        primary_idx = primary_idx.saturating_sub(1);\n                    }\n                    if active_monitor_idx >= idx {\n                        // Update active_monitor_idx to either still point at the same monitor, or\n                        // at some other monitor if the active monitor has\n                        // been removed.\n                        active_monitor_idx = active_monitor_idx.saturating_sub(1);\n                    }\n\n                    let primary = &mut monitors[primary_idx];\n                    primary.append_workspaces(workspaces);\n\n                    MonitorSet::Normal {\n                        monitors,\n                        primary_idx,\n                        active_monitor_idx,\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { .. } => {\n                panic!(\"tried to remove output when there were already none\")\n            }\n        }\n    }\n\n    pub fn add_column_by_idx(\n        &mut self,\n        monitor_idx: usize,\n        workspace_idx: usize,\n        column: Column<W>,\n        activate: bool,\n    ) {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        else {\n            panic!()\n        };\n\n        monitors[monitor_idx].add_column(workspace_idx, column, activate);\n\n        if activate {\n            *active_monitor_idx = monitor_idx;\n        }\n    }\n\n    /// Adds a new window to the layout.\n    ///\n    /// Returns an output that the window was added to, if there were any outputs.\n    #[allow(clippy::too_many_arguments)]\n    pub fn add_window(\n        &mut self,\n        window: W,\n        target: AddWindowTarget<W>,\n        width: Option<PresetSize>,\n        height: Option<PresetSize>,\n        is_full_width: bool,\n        is_floating: bool,\n        activate: ActivateWindow,\n    ) -> Option<&Output> {\n        let scrolling_height = height.map(SizeChange::from);\n        let id = window.id().clone();\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal {\n                monitors,\n                active_monitor_idx,\n                ..\n            } => {\n                let (mon_idx, target) = match target {\n                    AddWindowTarget::Auto => (*active_monitor_idx, MonitorAddWindowTarget::Auto),\n                    AddWindowTarget::Output(output) => {\n                        let mon_idx = monitors\n                            .iter()\n                            .position(|mon| mon.output == *output)\n                            .unwrap();\n\n                        (mon_idx, MonitorAddWindowTarget::Auto)\n                    }\n                    AddWindowTarget::Workspace(ws_id) => {\n                        let mon_idx = monitors\n                            .iter()\n                            .position(|mon| mon.workspaces.iter().any(|ws| ws.id() == ws_id))\n                            .unwrap();\n\n                        (\n                            mon_idx,\n                            MonitorAddWindowTarget::Workspace {\n                                id: ws_id,\n                                column_idx: None,\n                            },\n                        )\n                    }\n                    AddWindowTarget::NextTo(next_to) => {\n                        if let Some(output) = self\n                            .interactive_move\n                            .as_ref()\n                            .and_then(|move_| {\n                                if let InteractiveMoveState::Moving(move_) = move_ {\n                                    Some(move_)\n                                } else {\n                                    None\n                                }\n                            })\n                            .filter(|move_| next_to == move_.tile.window().id())\n                            .map(|move_| move_.output.clone())\n                        {\n                            // The next_to window is being interactively moved.\n                            let mon_idx = monitors\n                                .iter()\n                                .position(|mon| mon.output == output)\n                                .unwrap_or(*active_monitor_idx);\n\n                            (mon_idx, MonitorAddWindowTarget::Auto)\n                        } else {\n                            let mon_idx = monitors\n                                .iter()\n                                .position(|mon| {\n                                    mon.workspaces.iter().any(|ws| ws.has_window(next_to))\n                                })\n                                .unwrap();\n                            (mon_idx, MonitorAddWindowTarget::NextTo(next_to))\n                        }\n                    }\n                };\n                let mon = &mut monitors[mon_idx];\n\n                let (ws_idx, _) = mon.resolve_add_window_target(target);\n                let ws = &mon.workspaces[ws_idx];\n                let scrolling_width = ws.resolve_scrolling_width(&window, width);\n\n                mon.add_window(\n                    window,\n                    target,\n                    activate,\n                    scrolling_width,\n                    is_full_width,\n                    is_floating,\n                );\n\n                if activate.map_smart(|| false) {\n                    *active_monitor_idx = mon_idx;\n                }\n\n                // Set the default height for scrolling windows.\n                if !is_floating {\n                    if let Some(change) = scrolling_height {\n                        let ws = mon\n                            .workspaces\n                            .iter_mut()\n                            .find(|ws| ws.has_window(&id))\n                            .unwrap();\n                        ws.set_window_height(Some(&id), change);\n                    }\n                }\n\n                Some(&mon.output)\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                let (ws_idx, target) = match target {\n                    AddWindowTarget::Auto => {\n                        if workspaces.is_empty() {\n                            workspaces.push(Workspace::new_no_outputs(\n                                self.clock.clone(),\n                                self.options.clone(),\n                            ));\n                        }\n\n                        (0, WorkspaceAddWindowTarget::Auto)\n                    }\n                    AddWindowTarget::Output(_) => panic!(),\n                    AddWindowTarget::Workspace(ws_id) => {\n                        let ws_idx = workspaces.iter().position(|ws| ws.id() == ws_id).unwrap();\n                        (ws_idx, WorkspaceAddWindowTarget::Auto)\n                    }\n                    AddWindowTarget::NextTo(next_to) => {\n                        if self\n                            .interactive_move\n                            .as_ref()\n                            .and_then(|move_| {\n                                if let InteractiveMoveState::Moving(move_) = move_ {\n                                    Some(move_)\n                                } else {\n                                    None\n                                }\n                            })\n                            .filter(|move_| next_to == move_.tile.window().id())\n                            .is_some()\n                        {\n                            // The next_to window is being interactively moved. If there are no\n                            // other windows, we may have no workspaces at all.\n                            if workspaces.is_empty() {\n                                workspaces.push(Workspace::new_no_outputs(\n                                    self.clock.clone(),\n                                    self.options.clone(),\n                                ));\n                            }\n\n                            (0, WorkspaceAddWindowTarget::Auto)\n                        } else {\n                            let ws_idx = workspaces\n                                .iter()\n                                .position(|ws| ws.has_window(next_to))\n                                .unwrap();\n                            (ws_idx, WorkspaceAddWindowTarget::NextTo(next_to))\n                        }\n                    }\n                };\n                let ws = &mut workspaces[ws_idx];\n\n                let scrolling_width = ws.resolve_scrolling_width(&window, width);\n\n                let tile = ws.make_tile(window);\n                ws.add_tile(\n                    tile,\n                    target,\n                    activate,\n                    scrolling_width,\n                    is_full_width,\n                    is_floating,\n                );\n\n                // Set the default height for scrolling windows.\n                if !is_floating {\n                    if let Some(change) = scrolling_height {\n                        ws.set_window_height(Some(&id), change);\n                    }\n                }\n\n                None\n            }\n        }\n    }\n\n    pub fn remove_window(\n        &mut self,\n        window: &W::Id,\n        transaction: Transaction,\n    ) -> Option<RemovedTile<W>> {\n        if let Some(state) = &self.interactive_move {\n            match state {\n                InteractiveMoveState::Starting { window_id, .. } => {\n                    if window_id == window {\n                        self.interactive_move_end(window);\n                    }\n                }\n                InteractiveMoveState::Moving(move_) => {\n                    if move_.tile.window().id() == window {\n                        let Some(InteractiveMoveState::Moving(move_)) =\n                            self.interactive_move.take()\n                        else {\n                            unreachable!()\n                        };\n\n                        for mon in self.monitors_mut() {\n                            mon.dnd_scroll_gesture_end();\n                        }\n\n                        // Unlock the view on the workspaces.\n                        for ws in self.workspaces_mut() {\n                            ws.dnd_scroll_gesture_end();\n                        }\n\n                        return Some(RemovedTile {\n                            tile: move_.tile,\n                            width: move_.width,\n                            is_full_width: move_.is_full_width,\n                            is_floating: false,\n                        });\n                    }\n                }\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for (idx, ws) in mon.workspaces.iter_mut().enumerate() {\n                        if ws.has_window(window) {\n                            let removed = ws.remove_tile(window, transaction);\n\n                            // Clean up empty workspaces that are not active and not last.\n                            if !ws.has_windows_or_name()\n                                && idx != mon.active_workspace_idx\n                                && idx != mon.workspaces.len() - 1\n                                && mon.workspace_switch.is_none()\n                            {\n                                mon.workspaces.remove(idx);\n\n                                if idx < mon.active_workspace_idx {\n                                    mon.active_workspace_idx -= 1;\n                                }\n                            }\n\n                            // Special case handling when empty_workspace_above_first is set and all\n                            // workspaces are empty.\n                            if mon.options.layout.empty_workspace_above_first\n                                && mon.workspaces.len() == 2\n                                && mon.workspace_switch.is_none()\n                            {\n                                assert!(!mon.workspaces[0].has_windows_or_name());\n                                assert!(!mon.workspaces[1].has_windows_or_name());\n                                mon.workspaces.remove(1);\n                                mon.active_workspace_idx = 0;\n                            }\n                            return Some(removed);\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for (idx, ws) in workspaces.iter_mut().enumerate() {\n                    if ws.has_window(window) {\n                        let removed = ws.remove_tile(window, transaction);\n\n                        // Clean up empty workspaces.\n                        if !ws.has_windows_or_name() {\n                            workspaces.remove(idx);\n                        }\n\n                        return Some(removed);\n                    }\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn descendants_added(&mut self, id: &W::Id) -> bool {\n        for ws in self.workspaces_mut() {\n            if ws.descendants_added(id) {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    pub fn update_window(&mut self, window: &W::Id, serial: Option<Serial>) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if move_.tile.window().id() == window {\n                // Do this before calling update_window() so it can get up-to-date info.\n                if let Some(serial) = serial {\n                    move_.tile.window_mut().on_commit(serial);\n                }\n\n                move_.tile.update_window();\n                return;\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if ws.has_window(window) {\n                            ws.update_window(window, serial);\n                            return;\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    if ws.has_window(window) {\n                        ws.update_window(window, serial);\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn find_workspace_by_id(&self, id: WorkspaceId) -> Option<(usize, &Workspace<W>)> {\n        match &self.monitor_set {\n            MonitorSet::Normal { ref monitors, .. } => {\n                for mon in monitors {\n                    if let Some((index, workspace)) = mon\n                        .workspaces\n                        .iter()\n                        .enumerate()\n                        .find(|(_, w)| w.id() == id)\n                    {\n                        return Some((index, workspace));\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                if let Some((index, workspace)) =\n                    workspaces.iter().enumerate().find(|(_, w)| w.id() == id)\n                {\n                    return Some((index, workspace));\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn find_workspace_by_name(&self, workspace_name: &str) -> Option<(usize, &Workspace<W>)> {\n        match &self.monitor_set {\n            MonitorSet::Normal { ref monitors, .. } => {\n                for mon in monitors {\n                    if let Some((index, workspace)) =\n                        mon.workspaces.iter().enumerate().find(|(_, w)| {\n                            w.name\n                                .as_ref()\n                                .is_some_and(|name| name.eq_ignore_ascii_case(workspace_name))\n                        })\n                    {\n                        return Some((index, workspace));\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                if let Some((index, workspace)) = workspaces.iter().enumerate().find(|(_, w)| {\n                    w.name\n                        .as_ref()\n                        .is_some_and(|name| name.eq_ignore_ascii_case(workspace_name))\n                }) {\n                    return Some((index, workspace));\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn find_workspace_by_ref(\n        &mut self,\n        reference: WorkspaceReference,\n    ) -> Option<&mut Workspace<W>> {\n        if let WorkspaceReference::Index(index) = reference {\n            self.active_monitor().and_then(|m| {\n                let index = index.saturating_sub(1) as usize;\n                m.workspaces.get_mut(index)\n            })\n        } else {\n            self.workspaces_mut().find(|ws| match &reference {\n                WorkspaceReference::Name(ref_name) => ws\n                    .name\n                    .as_ref()\n                    .is_some_and(|name| name.eq_ignore_ascii_case(ref_name)),\n                WorkspaceReference::Id(id) => ws.id().get() == *id,\n                WorkspaceReference::Index(_) => unreachable!(),\n            })\n        }\n    }\n\n    pub fn unname_workspace(&mut self, workspace_name: &str) {\n        self.unname_workspace_by_ref(WorkspaceReference::Name(workspace_name.into()));\n    }\n\n    pub fn unname_workspace_by_ref(&mut self, reference: WorkspaceReference) {\n        let id = self.find_workspace_by_ref(reference).map(|ws| ws.id());\n        if let Some(id) = id {\n            self.unname_workspace_by_id(id);\n        }\n    }\n\n    pub fn unname_workspace_by_id(&mut self, id: WorkspaceId) {\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    if mon.unname_workspace(id) {\n                        return;\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                for (idx, ws) in workspaces.iter_mut().enumerate() {\n                    if ws.id() == id {\n                        ws.unname();\n\n                        // Clean up empty workspaces.\n                        if !ws.has_windows() {\n                            workspaces.remove(idx);\n                        }\n\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn find_window_and_output(&self, wl_surface: &WlSurface) -> Option<(&W, Option<&Output>)> {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().is_wl_surface(wl_surface) {\n                return Some((move_.tile.window(), Some(&move_.output)));\n            }\n        }\n\n        match &self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mon.workspaces {\n                        if let Some(window) = ws.find_wl_surface(wl_surface) {\n                            return Some((window, Some(&mon.output)));\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                for ws in workspaces {\n                    if let Some(window) = ws.find_wl_surface(wl_surface) {\n                        return Some((window, None));\n                    }\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn find_window_and_output_mut(\n        &mut self,\n        wl_surface: &WlSurface,\n    ) -> Option<(&mut W, Option<&Output>)> {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if move_.tile.window().is_wl_surface(wl_surface) {\n                return Some((move_.tile.window_mut(), Some(&move_.output)));\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if let Some(window) = ws.find_wl_surface_mut(wl_surface) {\n                            return Some((window, Some(&mon.output)));\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                for ws in workspaces {\n                    if let Some(window) = ws.find_wl_surface_mut(wl_surface) {\n                        return Some((window, None));\n                    }\n                }\n            }\n        }\n\n        None\n    }\n\n    /// Computes the window-geometry-relative target rect for popup unconstraining.\n    ///\n    /// We will try to fit popups inside this rect.\n    pub fn popup_target_rect(&self, window: &W::Id) -> Rectangle<f64, Logical> {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                // Follow the scrolling layout logic and fit the popup horizontally within the\n                // window geometry.\n                let width = move_.tile.window_size().w;\n                let height = output_size(&move_.output).h;\n                let mut target = Rectangle::from_size(Size::from((width, height)));\n                // FIXME: ideally this shouldn't include the tile render offset, but the code\n                // duplication would be a bit annoying for this edge case.\n                target.loc.y -= move_.tile_render_location(1.).y;\n                target.loc.y -= move_.tile.window_loc().y;\n                return target;\n            }\n        }\n\n        self.workspaces()\n            .find_map(|(_, _, ws)| ws.popup_target_rect(window))\n            .unwrap()\n    }\n\n    pub fn update_output_size(&mut self, output: &Output) {\n        let _span = tracy_client::span!(\"Layout::update_output_size\");\n\n        let Some(mon) = self.monitor_for_output_mut(output) else {\n            error!(\"monitor missing in update_output_size()\");\n            return;\n        };\n\n        mon.update_output_size();\n    }\n\n    pub fn scroll_amount_to_activate(&self, window: &W::Id) -> f64 {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                return 0.;\n            }\n        }\n\n        for mon in self.monitors() {\n            for ws in &mon.workspaces {\n                if ws.has_window(window) {\n                    return ws.scroll_amount_to_activate(window);\n                }\n            }\n        }\n\n        0.\n    }\n\n    pub fn should_trigger_focus_follows_mouse_on(&self, window: &W::Id) -> bool {\n        // During an animation, it's easy to trigger focus-follows-mouse on the previous workspace,\n        // especially when clicking to switch workspace on a bar of some kind. This cancels the\n        // workspace switch, which is annoying and not intended.\n        //\n        // This function allows focus-follows-mouse to trigger only on the animation target\n        // workspace.\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                return true;\n            }\n        }\n\n        let MonitorSet::Normal { monitors, .. } = &self.monitor_set else {\n            return true;\n        };\n\n        let (mon, ws_idx) = monitors\n            .iter()\n            .find_map(|mon| {\n                mon.workspaces\n                    .iter()\n                    .position(|ws| ws.has_window(window))\n                    .map(|ws_idx| (mon, ws_idx))\n            })\n            .unwrap();\n\n        // During a gesture, focus-follows-mouse does not cause any unintended workspace switches.\n        if let Some(WorkspaceSwitch::Gesture(_)) = mon.workspace_switch {\n            return true;\n        }\n\n        ws_idx == mon.active_workspace_idx\n    }\n\n    pub fn activate_window(&mut self, window: &W::Id) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                return;\n            }\n        }\n\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        else {\n            return;\n        };\n\n        for (monitor_idx, mon) in monitors.iter_mut().enumerate() {\n            for (workspace_idx, ws) in mon.workspaces.iter_mut().enumerate() {\n                if ws.activate_window(window) {\n                    *active_monitor_idx = monitor_idx;\n\n                    // If currently in the middle of a vertical swipe between the target workspace\n                    // and some other, don't switch the workspace.\n                    match &mon.workspace_switch {\n                        Some(WorkspaceSwitch::Gesture(gesture))\n                            if gesture.current_idx.floor() == workspace_idx as f64\n                                || gesture.current_idx.ceil() == workspace_idx as f64 => {}\n                        _ => mon.switch_workspace(workspace_idx),\n                    }\n\n                    return;\n                }\n            }\n        }\n    }\n\n    pub fn activate_window_without_raising(&mut self, window: &W::Id) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                return;\n            }\n        }\n\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        else {\n            return;\n        };\n\n        for (monitor_idx, mon) in monitors.iter_mut().enumerate() {\n            for (workspace_idx, ws) in mon.workspaces.iter_mut().enumerate() {\n                if ws.activate_window_without_raising(window) {\n                    *active_monitor_idx = monitor_idx;\n\n                    // If currently in the middle of a vertical swipe between the target workspace\n                    // and some other, don't switch the workspace.\n                    match &mon.workspace_switch {\n                        Some(WorkspaceSwitch::Gesture(gesture))\n                            if gesture.current_idx.floor() == workspace_idx as f64\n                                || gesture.current_idx.ceil() == workspace_idx as f64 => {}\n                        _ => mon.switch_workspace(workspace_idx),\n                    }\n\n                    return;\n                }\n            }\n        }\n    }\n\n    pub fn active_output(&self) -> Option<&Output> {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &self.monitor_set\n        else {\n            return None;\n        };\n\n        Some(&monitors[*active_monitor_idx].output)\n    }\n\n    pub fn active_workspace(&self) -> Option<&Workspace<W>> {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &self.monitor_set\n        else {\n            return None;\n        };\n\n        let mon = &monitors[*active_monitor_idx];\n        Some(&mon.workspaces[mon.active_workspace_idx])\n    }\n\n    pub fn active_workspace_mut(&mut self) -> Option<&mut Workspace<W>> {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        else {\n            return None;\n        };\n\n        let mon = &mut monitors[*active_monitor_idx];\n        Some(&mut mon.workspaces[mon.active_workspace_idx])\n    }\n\n    pub fn windows_for_output(&self, output: &Output) -> impl Iterator<Item = &W> + '_ {\n        let MonitorSet::Normal { monitors, .. } = &self.monitor_set else {\n            panic!()\n        };\n\n        let moving_window = self\n            .interactive_move\n            .as_ref()\n            .and_then(|x| x.moving())\n            .filter(|move_| move_.output == *output)\n            .map(|move_| move_.tile.window())\n            .into_iter();\n\n        let mon = monitors.iter().find(|mon| &mon.output == output).unwrap();\n        let mon_windows = mon.workspaces.iter().flat_map(|ws| ws.windows());\n\n        moving_window.chain(mon_windows)\n    }\n\n    pub fn windows_for_output_mut(&mut self, output: &Output) -> impl Iterator<Item = &mut W> + '_ {\n        let MonitorSet::Normal { monitors, .. } = &mut self.monitor_set else {\n            panic!()\n        };\n\n        let moving_window = self\n            .interactive_move\n            .as_mut()\n            .and_then(|x| x.moving_mut())\n            .filter(|move_| move_.output == *output)\n            .map(|move_| move_.tile.window_mut())\n            .into_iter();\n\n        let mon = monitors\n            .iter_mut()\n            .find(|mon| &mon.output == output)\n            .unwrap();\n        let mon_windows = mon.workspaces.iter_mut().flat_map(|ws| ws.windows_mut());\n\n        moving_window.chain(mon_windows)\n    }\n\n    pub fn with_windows(\n        &self,\n        mut f: impl FnMut(&W, Option<&Output>, Option<WorkspaceId>, WindowLayout),\n    ) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            // We don't fill any positions for interactively moved windows.\n            let layout = move_.tile.ipc_layout_template();\n            f(move_.tile.window(), Some(&move_.output), None, layout);\n        }\n\n        match &self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mon.workspaces {\n                        for (tile, layout) in ws.tiles_with_ipc_layouts() {\n                            f(tile.window(), Some(&mon.output), Some(ws.id()), layout);\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                for ws in workspaces {\n                    for (tile, layout) in ws.tiles_with_ipc_layouts() {\n                        f(tile.window(), None, Some(ws.id()), layout);\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn with_windows_mut(&mut self, mut f: impl FnMut(&mut W, Option<&Output>)) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            f(move_.tile.window_mut(), Some(&move_.output));\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        for win in ws.windows_mut() {\n                            f(win, Some(&mon.output));\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                for ws in workspaces {\n                    for win in ws.windows_mut() {\n                        f(win, None);\n                    }\n                }\n            }\n        }\n    }\n\n    fn active_monitor(&mut self) -> Option<&mut Monitor<W>> {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        else {\n            return None;\n        };\n\n        Some(&mut monitors[*active_monitor_idx])\n    }\n\n    pub fn active_monitor_ref(&self) -> Option<&Monitor<W>> {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &self.monitor_set\n        else {\n            return None;\n        };\n\n        Some(&monitors[*active_monitor_idx])\n    }\n\n    pub fn monitors(&self) -> impl Iterator<Item = &Monitor<W>> + '_ {\n        let monitors = if let MonitorSet::Normal { monitors, .. } = &self.monitor_set {\n            &monitors[..]\n        } else {\n            &[][..]\n        };\n\n        monitors.iter()\n    }\n\n    pub fn monitors_mut(&mut self) -> impl Iterator<Item = &mut Monitor<W>> + '_ {\n        let monitors = if let MonitorSet::Normal { monitors, .. } = &mut self.monitor_set {\n            &mut monitors[..]\n        } else {\n            &mut [][..]\n        };\n\n        monitors.iter_mut()\n    }\n\n    pub fn monitor_for_output(&self, output: &Output) -> Option<&Monitor<W>> {\n        self.monitors().find(|mon| &mon.output == output)\n    }\n\n    pub fn monitor_for_output_mut(&mut self, output: &Output) -> Option<&mut Monitor<W>> {\n        self.monitors_mut().find(|mon| &mon.output == output)\n    }\n\n    pub fn monitor_for_workspace(&self, workspace_name: &str) -> Option<&Monitor<W>> {\n        self.monitors().find(|monitor| {\n            monitor.workspaces.iter().any(|ws| {\n                ws.name\n                    .as_ref()\n                    .is_some_and(|name| name.eq_ignore_ascii_case(workspace_name))\n            })\n        })\n    }\n\n    pub fn outputs(&self) -> impl Iterator<Item = &Output> + '_ {\n        self.monitors().map(|mon| &mon.output)\n    }\n\n    pub fn move_left(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.move_left();\n    }\n\n    pub fn move_right(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.move_right();\n    }\n\n    pub fn move_column_to_first(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.move_column_to_first();\n    }\n\n    pub fn move_column_to_last(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.move_column_to_last();\n    }\n\n    pub fn move_column_left_or_to_output(&mut self, output: &Output) -> bool {\n        if let Some(workspace) = self.active_workspace_mut() {\n            if workspace.move_left() {\n                return false;\n            }\n        }\n\n        self.move_column_to_output(output, None, true);\n        true\n    }\n\n    pub fn move_column_right_or_to_output(&mut self, output: &Output) -> bool {\n        if let Some(workspace) = self.active_workspace_mut() {\n            if workspace.move_right() {\n                return false;\n            }\n        }\n\n        self.move_column_to_output(output, None, true);\n        true\n    }\n\n    pub fn move_column_to_index(&mut self, index: usize) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.move_column_to_index(index);\n    }\n\n    pub fn move_down(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.move_down();\n    }\n\n    pub fn move_up(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.move_up();\n    }\n\n    pub fn move_down_or_to_workspace_down(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_down_or_to_workspace_down();\n    }\n\n    pub fn move_up_or_to_workspace_up(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_up_or_to_workspace_up();\n    }\n\n    pub fn consume_or_expel_window_left(&mut self, window: Option<&W::Id>) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.consume_or_expel_window_left(window);\n    }\n\n    pub fn consume_or_expel_window_right(&mut self, window: Option<&W::Id>) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.consume_or_expel_window_right(window);\n    }\n\n    pub fn focus_left(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_left();\n    }\n\n    pub fn focus_right(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_right();\n    }\n\n    pub fn focus_column_first(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_column_first();\n    }\n\n    pub fn focus_column_last(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_column_last();\n    }\n\n    pub fn focus_column_right_or_first(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_column_right_or_first();\n    }\n\n    pub fn focus_column_left_or_last(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_column_left_or_last();\n    }\n\n    pub fn focus_column(&mut self, index: usize) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_column(index);\n    }\n\n    pub fn focus_window_up_or_output(&mut self, output: &Output) -> bool {\n        if let Some(workspace) = self.active_workspace_mut() {\n            if workspace.focus_up() {\n                return false;\n            }\n        }\n\n        self.focus_output(output);\n        true\n    }\n\n    pub fn focus_window_down_or_output(&mut self, output: &Output) -> bool {\n        if let Some(workspace) = self.active_workspace_mut() {\n            if workspace.focus_down() {\n                return false;\n            }\n        }\n\n        self.focus_output(output);\n        true\n    }\n\n    pub fn focus_column_left_or_output(&mut self, output: &Output) -> bool {\n        if let Some(workspace) = self.active_workspace_mut() {\n            if workspace.focus_left() {\n                return false;\n            }\n        }\n\n        self.focus_output(output);\n        true\n    }\n\n    pub fn focus_column_right_or_output(&mut self, output: &Output) -> bool {\n        if let Some(workspace) = self.active_workspace_mut() {\n            if workspace.focus_right() {\n                return false;\n            }\n        }\n\n        self.focus_output(output);\n        true\n    }\n\n    pub fn focus_window_in_column(&mut self, index: u8) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_window_in_column(index);\n    }\n\n    pub fn focus_down(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_down();\n    }\n\n    pub fn focus_up(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_up();\n    }\n\n    pub fn focus_down_or_left(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_down_or_left();\n    }\n\n    pub fn focus_down_or_right(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_down_or_right();\n    }\n\n    pub fn focus_up_or_left(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_up_or_left();\n    }\n\n    pub fn focus_up_or_right(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_up_or_right();\n    }\n\n    pub fn focus_window_or_workspace_down(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.focus_window_or_workspace_down();\n    }\n\n    pub fn focus_window_or_workspace_up(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.focus_window_or_workspace_up();\n    }\n\n    pub fn focus_window_top(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_window_top();\n    }\n\n    pub fn focus_window_bottom(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_window_bottom();\n    }\n\n    pub fn focus_window_down_or_top(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_window_down_or_top();\n    }\n\n    pub fn focus_window_up_or_bottom(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_window_up_or_bottom();\n    }\n\n    pub fn move_to_workspace_up(&mut self, focus: bool) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_to_workspace_up(focus);\n    }\n\n    pub fn move_to_workspace_down(&mut self, focus: bool) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_to_workspace_down(focus);\n    }\n\n    pub fn move_to_workspace(\n        &mut self,\n        window: Option<&W::Id>,\n        idx: usize,\n        activate: ActivateWindow,\n    ) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let monitor = if let Some(window) = window {\n            match &mut self.monitor_set {\n                MonitorSet::Normal { monitors, .. } => monitors\n                    .iter_mut()\n                    .find(|mon| mon.has_window(window))\n                    .unwrap(),\n                MonitorSet::NoOutputs { .. } => {\n                    return;\n                }\n            }\n        } else {\n            let Some(monitor) = self.active_monitor() else {\n                return;\n            };\n            monitor\n        };\n        monitor.move_to_workspace(window, idx, activate);\n    }\n\n    pub fn move_column_to_workspace_up(&mut self, activate: bool) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_column_to_workspace_up(activate);\n    }\n\n    pub fn move_column_to_workspace_down(&mut self, activate: bool) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_column_to_workspace_down(activate);\n    }\n\n    pub fn move_column_to_workspace(&mut self, idx: usize, activate: bool) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_column_to_workspace(idx, activate);\n    }\n\n    pub fn switch_workspace_up(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.switch_workspace_up();\n    }\n\n    pub fn switch_workspace_down(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.switch_workspace_down();\n    }\n\n    pub fn switch_workspace(&mut self, idx: usize) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.switch_workspace(idx);\n    }\n\n    pub fn switch_workspace_auto_back_and_forth(&mut self, idx: usize) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.switch_workspace_auto_back_and_forth(idx);\n    }\n\n    pub fn switch_workspace_previous(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.switch_workspace_previous();\n    }\n\n    pub fn consume_into_column(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.consume_into_column();\n    }\n\n    pub fn expel_from_column(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.expel_from_column();\n    }\n\n    pub fn swap_window_in_direction(&mut self, direction: ScrollDirection) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.swap_window_in_direction(direction);\n    }\n\n    pub fn toggle_column_tabbed_display(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.toggle_column_tabbed_display();\n    }\n\n    pub fn set_column_display(&mut self, display: ColumnDisplay) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.set_column_display(display);\n    }\n\n    pub fn center_column(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.center_column();\n    }\n\n    pub fn center_window(&mut self, id: Option<&W::Id>) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if id.is_none() || id == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(id) = id {\n            Some(self.workspaces_mut().find(|ws| ws.has_window(id)).unwrap())\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.center_window(id);\n    }\n\n    pub fn center_visible_columns(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.center_visible_columns();\n    }\n\n    pub fn focus(&self) -> Option<&W> {\n        self.focus_with_output().map(|(win, _out)| win)\n    }\n\n    pub fn focus_with_output(&self) -> Option<(&W, &Output)> {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            return Some((move_.tile.window(), &move_.output));\n        }\n\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &self.monitor_set\n        else {\n            return None;\n        };\n\n        let mon = &monitors[*active_monitor_idx];\n        mon.active_window().map(|win| (win, &mon.output))\n    }\n\n    pub fn interactive_moved_window_under(\n        &self,\n        output: &Output,\n        pos_within_output: Point<f64, Logical>,\n    ) -> Option<(&W, HitType)> {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.output == *output {\n                if self.overview_progress.is_some() {\n                    let zoom = self.overview_zoom();\n                    let tile_pos = move_.tile_render_location(zoom);\n                    let pos_within_tile = (pos_within_output - tile_pos).downscale(zoom);\n                    // During the overview animation, we cannot do input hits because we cannot\n                    // really represent scaled windows properly.\n                    let (win, hit) =\n                        HitType::hit_tile(&move_.tile, Point::from((0., 0.)), pos_within_tile)?;\n                    Some((win, hit.to_activate()))\n                } else {\n                    let tile_pos = move_.tile_render_location(1.);\n                    HitType::hit_tile(&move_.tile, tile_pos, pos_within_output)\n                }\n            } else {\n                None\n            }\n        } else {\n            None\n        }\n    }\n\n    /// Returns the window under the cursor and the hit type.\n    pub fn window_under(\n        &self,\n        output: &Output,\n        pos_within_output: Point<f64, Logical>,\n    ) -> Option<(&W, HitType)> {\n        let mon = self.monitor_for_output(output)?;\n        mon.window_under(pos_within_output)\n    }\n\n    pub fn resize_edges_under(\n        &self,\n        output: &Output,\n        pos_within_output: Point<f64, Logical>,\n    ) -> Option<ResizeEdge> {\n        let mon = self.monitor_for_output(output)?;\n        mon.resize_edges_under(pos_within_output)\n    }\n\n    pub fn workspace_under(\n        &self,\n        extended_bounds: bool,\n        output: &Output,\n        pos_within_output: Point<f64, Logical>,\n    ) -> Option<&Workspace<W>> {\n        if self\n            .interactive_moved_window_under(output, pos_within_output)\n            .is_some()\n        {\n            return None;\n        }\n\n        let mon = self.monitor_for_output(output)?;\n        if extended_bounds {\n            mon.workspace_under(pos_within_output).map(|(ws, _)| ws)\n        } else {\n            mon.workspace_under_narrow(pos_within_output)\n        }\n    }\n\n    pub fn overview_zoom(&self) -> f64 {\n        let progress = self.overview_progress.as_ref().map(|p| p.value());\n        compute_overview_zoom(&self.options, progress)\n    }\n\n    #[cfg(test)]\n    fn verify_invariants(&self) {\n        use std::collections::HashSet;\n\n        use approx::assert_abs_diff_eq;\n\n        let zoom = self.overview_zoom();\n\n        let mut move_win_id = None;\n        if let Some(state) = &self.interactive_move {\n            match state {\n                InteractiveMoveState::Starting {\n                    window_id,\n                    pointer_delta: _,\n                    pointer_ratio_within_window: _,\n                } => {\n                    assert!(\n                        self.has_window(window_id),\n                        \"interactive move must be on an existing window\"\n                    );\n                    move_win_id = Some(window_id.clone());\n                }\n                InteractiveMoveState::Moving(move_) => {\n                    assert_eq!(self.clock, move_.tile.clock);\n                    assert!(move_.tile.window().pending_sizing_mode().is_normal());\n\n                    move_.tile.verify_invariants();\n\n                    let scale = move_.output.current_scale().fractional_scale();\n                    let options = Options::clone(&self.options)\n                        .with_merged_layout(move_.output_config.as_ref())\n                        .with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))\n                        .adjusted_for_scale(scale);\n                    assert_eq!(\n                        &*move_.tile.options, &options,\n                        \"interactive moved tile options must be \\\n                         base options adjusted for output scale\"\n                    );\n\n                    let tile_pos = move_.tile_render_location(zoom);\n                    let rounded_pos = tile_pos.to_physical_precise_round(scale).to_logical(scale);\n\n                    // Tile position must be rounded to physical pixels.\n                    assert_abs_diff_eq!(tile_pos.x, rounded_pos.x, epsilon = 1e-5);\n                    assert_abs_diff_eq!(tile_pos.y, rounded_pos.y, epsilon = 1e-5);\n\n                    if let Some(alpha) = &move_.tile.alpha_animation {\n                        if move_.is_floating {\n                            assert_eq!(\n                                alpha.anim.to(),\n                                1.,\n                                \"interactively moved floating tile can animate alpha only to 1\"\n                            );\n\n                            assert!(\n                                !alpha.hold_after_done,\n                                \"interactively moved floating tile \\\n                                 cannot have held alpha animation\"\n                            );\n                        } else {\n                            assert_ne!(\n                                alpha.anim.to(),\n                                1.,\n                                \"interactively moved scrolling tile must animate alpha to not 1\"\n                            );\n\n                            assert!(\n                                alpha.hold_after_done,\n                                \"interactively moved scrolling tile \\\n                                 must have held alpha animation\"\n                            );\n                        }\n                    }\n                }\n            }\n        }\n\n        let mut seen_workspace_id = HashSet::new();\n        let mut seen_workspace_name = Vec::<String>::new();\n\n        let (monitors, &primary_idx, &active_monitor_idx) = match &self.monitor_set {\n            MonitorSet::Normal {\n                monitors,\n                primary_idx,\n                active_monitor_idx,\n            } => (monitors, primary_idx, active_monitor_idx),\n            MonitorSet::NoOutputs { workspaces } => {\n                for workspace in workspaces {\n                    assert!(\n                        workspace.has_windows_or_name(),\n                        \"with no outputs there cannot be empty unnamed workspaces\"\n                    );\n\n                    assert_eq!(self.clock, workspace.clock);\n\n                    assert_eq!(\n                        workspace.base_options, self.options,\n                        \"workspace base options must be synchronized with layout\"\n                    );\n\n                    assert!(\n                        seen_workspace_id.insert(workspace.id()),\n                        \"workspace id must be unique\"\n                    );\n\n                    if let Some(name) = &workspace.name {\n                        assert!(\n                            !seen_workspace_name\n                                .iter()\n                                .any(|n| n.eq_ignore_ascii_case(name)),\n                            \"workspace name must be unique\"\n                        );\n                        seen_workspace_name.push(name.clone());\n                    }\n\n                    workspace.verify_invariants(move_win_id.as_ref());\n                }\n\n                return;\n            }\n        };\n\n        assert!(primary_idx < monitors.len());\n        assert!(active_monitor_idx < monitors.len());\n\n        let mut saw_view_offset_gesture = false;\n\n        for (idx, monitor) in monitors.iter().enumerate() {\n            assert_eq!(self.clock, monitor.clock);\n            assert_eq!(\n                monitor.base_options, self.options,\n                \"monitor base options must be synchronized with layout\"\n            );\n\n            assert_eq!(self.overview_open, monitor.overview_open);\n            assert_eq!(\n                self.overview_progress.as_ref().map(|p| p.value()),\n                monitor.overview_progress_value()\n            );\n\n            monitor.verify_invariants();\n\n            if idx == primary_idx {\n                for ws in &monitor.workspaces {\n                    if ws.original_output.matches(&monitor.output) {\n                        // This is the primary monitor's own workspace.\n                        continue;\n                    }\n\n                    let own_monitor_exists = monitors\n                        .iter()\n                        .any(|m| ws.original_output.matches(&m.output));\n                    assert!(\n                        !own_monitor_exists,\n                        \"primary monitor cannot have workspaces for which their own monitor exists\"\n                    );\n                }\n            } else {\n                assert!(\n                    monitor\n                        .workspaces\n                        .iter()\n                        .any(|workspace| workspace.original_output.matches(&monitor.output)),\n                    \"secondary monitor must not have any non-own workspaces\"\n                );\n            }\n\n            // FIXME: verify that primary doesn't have any workspaces for which their own monitor\n            // exists.\n\n            for workspace in &monitor.workspaces {\n                assert!(\n                    seen_workspace_id.insert(workspace.id()),\n                    \"workspace id must be unique\"\n                );\n\n                if let Some(name) = &workspace.name {\n                    assert!(\n                        !seen_workspace_name\n                            .iter()\n                            .any(|n| n.eq_ignore_ascii_case(name)),\n                        \"workspace name must be unique\"\n                    );\n                    seen_workspace_name.push(name.clone());\n                }\n\n                workspace.verify_invariants(move_win_id.as_ref());\n\n                let has_view_offset_gesture = workspace.scrolling().view_offset().is_gesture();\n                if self.dnd.is_some() || self.interactive_move.is_some() {\n                    // We'd like to check that all workspaces have the gesture here, furthermore we\n                    // want to check that they have the gesture only if the interactive move\n                    // targets the scrolling layout. However, we cannot do that because we start\n                    // and stop the gesture lazily. Otherwise the gesture code would pollute a lot\n                    // of places like adding new workspaces, implicitly moving windows between\n                    // floating and tiling on fullscreen, etc.\n                    //\n                    // assert!(\n                    //     has_view_offset_gesture,\n                    //     \"during an interactive move in the scrolling layout, \\\n                    //      all workspaces should be in a view offset gesture\"\n                    // );\n                } else if saw_view_offset_gesture {\n                    assert!(\n                        !has_view_offset_gesture,\n                        \"only one workspace can have an ongoing view offset gesture\"\n                    );\n                }\n                saw_view_offset_gesture = has_view_offset_gesture;\n            }\n        }\n    }\n\n    pub fn advance_animations(&mut self) {\n        let _span = tracy_client::span!(\"Layout::advance_animations\");\n\n        let mut dnd_scroll = None;\n        let mut is_dnd = false;\n        if let Some(dnd) = &self.dnd {\n            dnd_scroll = Some((dnd.output.clone(), dnd.pointer_pos_within_output, true));\n            is_dnd = true;\n        }\n\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            move_.tile.advance_animations();\n\n            if dnd_scroll.is_none() {\n                dnd_scroll = Some((\n                    move_.output.clone(),\n                    move_.pointer_pos_within_output,\n                    !move_.is_floating,\n                ));\n            }\n        }\n\n        let is_overview_open = self.overview_open;\n\n        // Scroll the view if needed.\n        if let Some((output, pos_within_output, is_scrolling)) = dnd_scroll {\n            if let Some(mon) = self.monitor_for_output_mut(&output) {\n                let mut scrolled = false;\n\n                let zoom = mon.overview_zoom();\n                scrolled |= mon.dnd_scroll_gesture_scroll(pos_within_output, 1. / zoom);\n\n                if is_scrolling {\n                    if let Some((ws, geo)) = mon.workspace_under(pos_within_output) {\n                        let ws_id = ws.id();\n                        let ws = mon\n                            .workspaces\n                            .iter_mut()\n                            .find(|ws| ws.id() == ws_id)\n                            .unwrap();\n                        // As far as the DnD scroll gesture is concerned, the workspace spans across\n                        // the whole monitor horizontally.\n                        let ws_pos = Point::from((0., geo.loc.y));\n                        scrolled |=\n                            ws.dnd_scroll_gesture_scroll(pos_within_output - ws_pos, 1. / zoom);\n                    }\n                }\n\n                if scrolled {\n                    // Don't trigger DnD hold while scrolling.\n                    if let Some(dnd) = &mut self.dnd {\n                        dnd.hold = None;\n                    }\n                } else if is_dnd {\n                    let target = mon\n                        .window_under(pos_within_output)\n                        .map(|(win, _)| DndHoldTarget::Window(win.id().clone()))\n                        .or_else(|| {\n                            mon.workspace_under_narrow(pos_within_output)\n                                .map(|ws| DndHoldTarget::Workspace(ws.id()))\n                        });\n\n                    let dnd = self.dnd.as_mut().unwrap();\n                    if let Some(target) = target {\n                        let now = self.clock.now_unadjusted();\n                        let start_time = if let Some(hold) = &mut dnd.hold {\n                            if hold.target != target {\n                                hold.start_time = now;\n                            }\n                            hold.target = target;\n                            hold.start_time\n                        } else {\n                            let hold = dnd.hold.insert(DndHold {\n                                start_time: now,\n                                target,\n                            });\n                            hold.start_time\n                        };\n\n                        // Delay copied from gnome-shell.\n                        let delay = Duration::from_millis(750);\n                        if delay <= now.saturating_sub(start_time) {\n                            let hold = dnd.hold.take().unwrap();\n\n                            // Synchronize workspace switch to overview close to get a monotonic\n                            // animation.\n                            let config = is_overview_open\n                                .then_some(self.options.animations.overview_open_close.0);\n\n                            let mon = self.monitor_for_output_mut(&output).unwrap();\n\n                            let ws_idx = match hold.target {\n                                DndHoldTarget::Window(id) => mon\n                                    .workspaces\n                                    .iter_mut()\n                                    .position(|ws| ws.activate_window(&id))\n                                    .unwrap(),\n                                DndHoldTarget::Workspace(id) => {\n                                    mon.workspaces.iter().position(|ws| ws.id() == id).unwrap()\n                                }\n                            };\n\n                            mon.dnd_scroll_gesture_end();\n                            mon.activate_workspace_with_anim_config(ws_idx, config);\n\n                            self.focus_output(&output);\n\n                            if is_overview_open {\n                                self.close_overview();\n                            }\n                        }\n                    } else {\n                        // No target, reset the hold timer.\n                        dnd.hold = None;\n                    }\n                }\n            }\n        }\n\n        if let Some(OverviewProgress::Animation(anim)) = &mut self.overview_progress {\n            if anim.is_done() {\n                if self.overview_open {\n                    self.overview_progress = Some(OverviewProgress::Open);\n                } else {\n                    self.overview_progress = None;\n                }\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    mon.set_overview_progress(self.overview_progress.as_ref());\n                    mon.advance_animations();\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    ws.advance_animations();\n                }\n            }\n        }\n    }\n\n    pub fn are_animations_ongoing(&self, output: Option<&Output>) -> bool {\n        // Keep advancing animations if we might need to scroll the view.\n        if let Some(dnd) = &self.dnd {\n            if output.is_none_or(|output| *output == dnd.output) {\n                return true;\n            }\n        }\n\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if output.is_none_or(|output| *output == move_.output) {\n                if move_.tile.are_animations_ongoing() {\n                    return true;\n                }\n\n                // Keep advancing animations if we might need to scroll the view.\n                if !move_.is_floating || self.overview_open {\n                    return true;\n                }\n            }\n        }\n\n        if self\n            .overview_progress\n            .as_ref()\n            .is_some_and(|p| p.is_animation())\n        {\n            return true;\n        }\n\n        for mon in self.monitors() {\n            if output.is_some_and(|output| mon.output != *output) {\n                continue;\n            }\n\n            if mon.are_animations_ongoing() {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    pub fn update_render_elements(&mut self, output: Option<&Output>) {\n        let _span = tracy_client::span!(\"Layout::update_render_elements\");\n\n        self.update_render_elements_time = self.clock.now();\n\n        let zoom = self.overview_zoom();\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if output.is_none_or(|output| move_.output == *output) {\n                let pos_within_output = move_.tile_render_location(zoom);\n                let view_rect =\n                    Rectangle::new(pos_within_output.upscale(-1.), output_size(&move_.output));\n                move_.tile.update_render_elements(true, view_rect);\n            }\n        }\n\n        self.update_insert_hint(output);\n\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        else {\n            error!(\"update_render_elements called with no monitors\");\n            return;\n        };\n\n        for (idx, mon) in monitors.iter_mut().enumerate() {\n            if output.is_none_or(|output| mon.output == *output) {\n                let is_active = self.is_active\n                    && idx == *active_monitor_idx\n                    && !matches!(self.interactive_move, Some(InteractiveMoveState::Moving(_)));\n                mon.set_overview_progress(self.overview_progress.as_ref());\n                mon.update_render_elements(is_active);\n            }\n        }\n    }\n\n    pub fn update_shaders(&mut self) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            move_.tile.update_shaders();\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    mon.update_shaders();\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    ws.update_shaders();\n                }\n            }\n        }\n    }\n\n    fn update_insert_hint(&mut self, output: Option<&Output>) {\n        let _span = tracy_client::span!(\"Layout::update_insert_hint\");\n\n        for mon in self.monitors_mut() {\n            mon.insert_hint = None;\n        }\n\n        if !matches!(self.interactive_move, Some(InteractiveMoveState::Moving(_))) {\n            return;\n        }\n        let Some(InteractiveMoveState::Moving(move_)) = self.interactive_move.take() else {\n            unreachable!()\n        };\n        if output.is_some_and(|out| &move_.output != out) {\n            self.interactive_move = Some(InteractiveMoveState::Moving(move_));\n            return;\n        }\n\n        let _span = tracy_client::span!(\"Layout::update_insert_hint::update\");\n\n        if let Some(mon) = self.monitor_for_output_mut(&move_.output) {\n            let zoom = mon.overview_zoom();\n            let (insert_ws, geo) = mon.insert_position(move_.pointer_pos_within_output);\n            match insert_ws {\n                InsertWorkspace::Existing(ws_id) => {\n                    let ws = mon\n                        .workspaces\n                        .iter_mut()\n                        .find(|ws| ws.id() == ws_id)\n                        .unwrap();\n                    let pos_within_workspace =\n                        (move_.pointer_pos_within_output - geo.loc).downscale(zoom);\n                    let position = if move_.is_floating {\n                        InsertPosition::Floating\n                    } else {\n                        ws.scrolling_insert_position(pos_within_workspace)\n                    };\n\n                    let rules = move_.tile.window().rules();\n                    let border_width = move_.tile.effective_border_width().unwrap_or(0.);\n                    let corner_radius = rules\n                        .geometry_corner_radius\n                        .map_or(CornerRadius::default(), |radius| {\n                            radius.expanded_by(border_width as f32)\n                        });\n                    mon.insert_hint = Some(InsertHint {\n                        workspace: insert_ws,\n                        position,\n                        corner_radius,\n                    });\n                }\n                InsertWorkspace::NewAt(_) => {\n                    let position = if move_.is_floating {\n                        InsertPosition::Floating\n                    } else {\n                        InsertPosition::NewColumn(0)\n                    };\n                    mon.insert_hint = Some(InsertHint {\n                        workspace: insert_ws,\n                        position,\n                        corner_radius: CornerRadius::default(),\n                    });\n                }\n            }\n        }\n\n        self.interactive_move = Some(InteractiveMoveState::Moving(move_));\n    }\n\n    pub fn ensure_named_workspace(&mut self, ws_config: &WorkspaceConfig) {\n        if self.find_workspace_by_name(&ws_config.name.0).is_some() {\n            return;\n        }\n\n        let clock = self.clock.clone();\n        let options = self.options.clone();\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal {\n                monitors,\n                primary_idx,\n                active_monitor_idx,\n            } => {\n                let mon_idx = ws_config\n                    .open_on_output\n                    .as_deref()\n                    .map(|name| {\n                        monitors\n                            .iter_mut()\n                            .position(|monitor| output_matches_name(&monitor.output, name))\n                            .unwrap_or(*primary_idx)\n                    })\n                    .unwrap_or(*active_monitor_idx);\n                let mon = &mut monitors[mon_idx];\n\n                let ws = Workspace::new_with_config(\n                    mon.output.clone(),\n                    Some(ws_config.clone()),\n                    clock,\n                    options,\n                );\n                mon.insert_workspace(ws, 0, false);\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                let ws =\n                    Workspace::new_with_config_no_outputs(Some(ws_config.clone()), clock, options);\n                workspaces.insert(0, ws);\n            }\n        }\n    }\n\n    pub fn update_config(&mut self, config: &Config) {\n        // Update workspace-specific config for all named workspaces.\n        for ws in self.workspaces_mut() {\n            let Some(name) = ws.name() else { continue };\n            if let Some(config) = config.workspaces.iter().find(|w| &w.name.0 == name) {\n                ws.update_layout_config(config.layout.clone().map(|x| x.0));\n            }\n        }\n\n        self.update_options(Options::from_config(config));\n    }\n\n    fn update_options(&mut self, options: Options) {\n        let options = Rc::new(options);\n\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            let view_size = output_size(&move_.output);\n            let scale = move_.output.current_scale().fractional_scale();\n            let options = Options::clone(&options)\n                .with_merged_layout(move_.output_config.as_ref())\n                .with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))\n                .adjusted_for_scale(scale);\n            move_.tile.update_config(view_size, scale, Rc::new(options));\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    mon.update_config(options.clone());\n                }\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                for ws in workspaces {\n                    ws.update_config(options.clone());\n                }\n            }\n        }\n\n        self.options = options;\n    }\n\n    pub fn toggle_width(&mut self, forwards: bool) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.toggle_width(forwards);\n    }\n\n    pub fn toggle_window_width(&mut self, window: Option<&W::Id>, forwards: bool) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.toggle_window_width(window, forwards);\n    }\n\n    pub fn toggle_window_height(&mut self, window: Option<&W::Id>, forwards: bool) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.toggle_window_height(window, forwards);\n    }\n\n    pub fn toggle_full_width(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.toggle_full_width();\n    }\n\n    pub fn set_column_width(&mut self, change: SizeChange) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.set_column_width(change);\n    }\n\n    pub fn set_window_width(&mut self, window: Option<&W::Id>, change: SizeChange) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.set_window_width(window, change);\n    }\n\n    pub fn set_window_height(&mut self, window: Option<&W::Id>, change: SizeChange) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.set_window_height(window, change);\n    }\n\n    pub fn reset_window_height(&mut self, window: Option<&W::Id>) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.reset_window_height(window);\n    }\n\n    pub fn expand_column_to_available_width(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.expand_column_to_available_width();\n    }\n\n    pub fn toggle_window_floating(&mut self, window: Option<&W::Id>) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                move_.is_floating = !move_.is_floating;\n\n                // When going to floating, restore the floating window size.\n                if move_.is_floating {\n                    let floating_size = move_.tile.floating_window_size;\n                    let win = move_.tile.window_mut();\n                    let mut size =\n                        floating_size.unwrap_or_else(|| win.expected_size().unwrap_or_default());\n\n                    // Apply min/max size window rules. If requesting a concrete size, apply\n                    // completely; if requesting (0, 0), apply only when min/max results in a fixed\n                    // size.\n                    let min_size = win.min_size();\n                    let max_size = win.max_size();\n                    size.w = ensure_min_max_size_maybe_zero(size.w, min_size.w, max_size.w);\n                    size.h = ensure_min_max_size_maybe_zero(size.h, min_size.h, max_size.h);\n\n                    win.request_size_once(size, true);\n\n                    // Animate the tile back to opaque.\n                    move_.tile.animate_alpha(\n                        INTERACTIVE_MOVE_ALPHA,\n                        1.,\n                        self.options.animations.window_movement.0,\n                    );\n\n                    // Unlock the view on the workspaces.\n                    for ws in self.workspaces_mut() {\n                        ws.dnd_scroll_gesture_end();\n                    }\n                } else {\n                    // Animate the tile back to semitransparent.\n                    move_.tile.animate_alpha(\n                        1.,\n                        INTERACTIVE_MOVE_ALPHA,\n                        self.options.animations.window_movement.0,\n                    );\n                    move_.tile.hold_alpha_animation_after_done();\n                }\n\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.toggle_window_floating(window);\n    }\n\n    pub fn set_window_floating(&mut self, window: Option<&W::Id>, floating: bool) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                if move_.is_floating != floating {\n                    self.toggle_window_floating(window);\n                }\n                return;\n            }\n        }\n\n        let workspace = if let Some(window) = window {\n            Some(\n                self.workspaces_mut()\n                    .find(|ws| ws.has_window(window))\n                    .unwrap(),\n            )\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.set_window_floating(window, floating);\n    }\n\n    pub fn focus_floating(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_floating();\n    }\n\n    pub fn focus_tiling(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.focus_tiling();\n    }\n\n    pub fn switch_focus_floating_tiling(&mut self) {\n        let Some(workspace) = self.active_workspace_mut() else {\n            return;\n        };\n        workspace.switch_focus_floating_tiling();\n    }\n\n    pub fn move_floating_window(\n        &mut self,\n        id: Option<&W::Id>,\n        x: PositionChange,\n        y: PositionChange,\n        animate: bool,\n    ) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if id.is_none() || id == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        let workspace = if let Some(id) = id {\n            Some(self.workspaces_mut().find(|ws| ws.has_window(id)).unwrap())\n        } else {\n            self.active_workspace_mut()\n        };\n\n        let Some(workspace) = workspace else {\n            return;\n        };\n        workspace.move_floating_window(id, x, y, animate);\n    }\n\n    pub fn focus_output(&mut self, output: &Output) {\n        if let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        {\n            for (idx, mon) in monitors.iter().enumerate() {\n                if &mon.output == output {\n                    *active_monitor_idx = idx;\n                    return;\n                }\n            }\n        }\n    }\n\n    pub fn move_to_output(\n        &mut self,\n        window: Option<&W::Id>,\n        output: &Output,\n        target_ws_idx: Option<usize>,\n        activate: ActivateWindow,\n    ) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if window.is_none() || window == Some(move_.tile.window().id()) {\n                return;\n            }\n        }\n\n        if let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        {\n            let new_idx = monitors\n                .iter()\n                .position(|mon| &mon.output == output)\n                .unwrap();\n\n            let (mon_idx, ws_idx) = if let Some(window) = window {\n                monitors\n                    .iter()\n                    .enumerate()\n                    .find_map(|(mon_idx, mon)| {\n                        mon.workspaces\n                            .iter()\n                            .position(|ws| ws.has_window(window))\n                            .map(|ws_idx| (mon_idx, ws_idx))\n                    })\n                    .unwrap()\n            } else {\n                let mon_idx = *active_monitor_idx;\n                let mon = &monitors[mon_idx];\n                (mon_idx, mon.active_workspace_idx)\n            };\n\n            let workspace_idx = target_ws_idx.unwrap_or(monitors[new_idx].active_workspace_idx);\n            if mon_idx == new_idx && ws_idx == workspace_idx {\n                return;\n            }\n\n            let mon = &monitors[new_idx];\n            if mon.workspaces.len() <= workspace_idx {\n                return;\n            }\n\n            let ws_id = mon.workspaces[workspace_idx].id();\n\n            let mon = &mut monitors[mon_idx];\n            let activate = activate.map_smart(|| {\n                window.is_none_or(|win| {\n                    mon_idx == *active_monitor_idx\n                        && mon.active_window().map(|win| win.id()) == Some(win)\n                })\n            });\n            let activate = if activate {\n                ActivateWindow::Yes\n            } else {\n                ActivateWindow::No\n            };\n\n            let ws = &mut mon.workspaces[ws_idx];\n            let transaction = Transaction::new();\n            let mut removed = if let Some(window) = window {\n                ws.remove_tile(window, transaction)\n            } else if let Some(removed) = ws.remove_active_tile(transaction) {\n                removed\n            } else {\n                return;\n            };\n\n            removed.tile.stop_move_animations();\n\n            let mon = &mut monitors[new_idx];\n            mon.add_tile(\n                removed.tile,\n                MonitorAddWindowTarget::Workspace {\n                    id: ws_id,\n                    column_idx: None,\n                },\n                activate,\n                true,\n                removed.width,\n                removed.is_full_width,\n                removed.is_floating,\n            );\n            if activate.map_smart(|| false) {\n                *active_monitor_idx = new_idx;\n            }\n\n            let mon = &mut monitors[mon_idx];\n            if mon.workspace_switch.is_none() {\n                monitors[mon_idx].clean_up_workspaces();\n            }\n        }\n    }\n\n    pub fn move_column_to_output(\n        &mut self,\n        output: &Output,\n        target_ws_idx: Option<usize>,\n        activate: bool,\n    ) {\n        if let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        {\n            let new_idx = monitors\n                .iter()\n                .position(|mon| &mon.output == output)\n                .unwrap();\n\n            let current = &mut monitors[*active_monitor_idx];\n            let ws = current.active_workspace();\n\n            if ws.floating_is_active() {\n                self.move_to_output(None, output, None, ActivateWindow::Smart);\n                return;\n            }\n\n            let Some(column) = ws.remove_active_column() else {\n                return;\n            };\n\n            let workspace_idx = target_ws_idx\n                .unwrap_or(monitors[new_idx].active_workspace_idx)\n                .min(monitors[new_idx].workspaces.len() - 1);\n            self.add_column_by_idx(new_idx, workspace_idx, column, activate);\n        }\n    }\n\n    pub fn move_workspace_to_output(&mut self, output: &Output) -> bool {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &self.monitor_set\n        else {\n            return false;\n        };\n\n        let idx = monitors[*active_monitor_idx].active_workspace_idx;\n        self.move_workspace_to_output_by_id(idx, None, output)\n    }\n\n    // FIXME: accept workspace by id\n    pub fn move_workspace_to_output_by_id(\n        &mut self,\n        old_idx: usize,\n        old_output: Option<Output>,\n        new_output: &Output,\n    ) -> bool {\n        let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        else {\n            return false;\n        };\n\n        let current_idx = if let Some(old_output) = old_output {\n            monitors\n                .iter()\n                .position(|mon| mon.output == old_output)\n                .unwrap()\n        } else {\n            *active_monitor_idx\n        };\n        let target_idx = monitors\n            .iter()\n            .position(|mon| mon.output == *new_output)\n            .unwrap();\n\n        let current = &mut monitors[current_idx];\n\n        if current.workspaces.len() <= old_idx {\n            return false;\n        }\n\n        // Do not do anything if the output is already correct\n        if current_idx == target_idx {\n            // Just update the original output since this is an explicit movement action.\n            current.workspaces[old_idx].original_output = OutputId::new(&current.output);\n\n            return false;\n        }\n\n        // Only switch active monitor if the workspace to be moved is the currently focused one on\n        // the current monitor.\n        let activate =\n            current_idx == *active_monitor_idx && old_idx == current.active_workspace_idx;\n\n        let mut ws = current.remove_workspace_by_idx(old_idx);\n        ws.original_output = OutputId::new(new_output);\n\n        let target = &mut monitors[target_idx];\n        target.insert_workspace(ws, target.active_workspace_idx + 1, activate);\n\n        if activate {\n            *active_monitor_idx = target_idx;\n        }\n\n        activate\n    }\n\n    pub fn set_fullscreen(&mut self, id: &W::Id, is_fullscreen: bool) {\n        // Check if this is a request to unset the windowed fullscreen state.\n        if !is_fullscreen {\n            let mut handled = false;\n            self.with_windows_mut(|window, _| {\n                if window.id() == id && window.is_pending_windowed_fullscreen() {\n                    window.request_windowed_fullscreen(false);\n                    handled = true;\n                }\n            });\n            if handled {\n                return;\n            }\n        }\n\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == id {\n                return;\n            }\n        }\n\n        for ws in self.workspaces_mut() {\n            if ws.has_window(id) {\n                ws.set_fullscreen(id, is_fullscreen);\n                return;\n            }\n        }\n    }\n\n    pub fn toggle_fullscreen(&mut self, id: &W::Id) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == id {\n                return;\n            }\n        }\n\n        for ws in self.workspaces_mut() {\n            if ws.has_window(id) {\n                ws.toggle_fullscreen(id);\n                return;\n            }\n        }\n    }\n\n    pub fn toggle_windowed_fullscreen(&mut self, id: &W::Id) {\n        let (_, window) = self.windows().find(|(_, win)| win.id() == id).unwrap();\n        if window.pending_sizing_mode().is_fullscreen() {\n            // Remove the real fullscreen.\n            for ws in self.workspaces_mut() {\n                if ws.has_window(id) {\n                    ws.set_fullscreen(id, false);\n                    break;\n                }\n            }\n        }\n\n        // This will switch is_pending_fullscreen() to false right away.\n        self.with_windows_mut(|window, _| {\n            if window.id() == id {\n                window.request_windowed_fullscreen(!window.is_pending_windowed_fullscreen());\n            }\n        });\n    }\n\n    pub fn set_maximized(&mut self, id: &W::Id, maximize: bool) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == id {\n                return;\n            }\n        }\n\n        for ws in self.workspaces_mut() {\n            if ws.has_window(id) {\n                ws.set_maximized(id, maximize);\n                return;\n            }\n        }\n    }\n\n    pub fn toggle_maximized(&mut self, id: &W::Id) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == id {\n                return;\n            }\n        }\n\n        for ws in self.workspaces_mut() {\n            if ws.has_window(id) {\n                ws.toggle_maximized(id);\n                return;\n            }\n        }\n    }\n\n    pub fn workspace_switch_gesture_begin(&mut self, output: &Output, is_touchpad: bool) {\n        let monitors = match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => monitors,\n            MonitorSet::NoOutputs { .. } => unreachable!(),\n        };\n\n        for monitor in monitors {\n            // Cancel the gesture on other outputs.\n            if &monitor.output != output {\n                monitor.workspace_switch_gesture_end(None);\n                continue;\n            }\n\n            monitor.workspace_switch_gesture_begin(is_touchpad);\n        }\n    }\n\n    pub fn workspace_switch_gesture_update(\n        &mut self,\n        delta_y: f64,\n        timestamp: Duration,\n        is_touchpad: bool,\n    ) -> Option<Option<Output>> {\n        let monitors = match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => monitors,\n            MonitorSet::NoOutputs { .. } => return None,\n        };\n\n        for monitor in monitors {\n            if let Some(refresh) =\n                monitor.workspace_switch_gesture_update(delta_y, timestamp, is_touchpad)\n            {\n                if refresh {\n                    return Some(Some(monitor.output.clone()));\n                } else {\n                    return Some(None);\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn workspace_switch_gesture_end(&mut self, is_touchpad: Option<bool>) -> Option<Output> {\n        let monitors = match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => monitors,\n            MonitorSet::NoOutputs { .. } => return None,\n        };\n\n        for monitor in monitors {\n            if monitor.workspace_switch_gesture_end(is_touchpad) {\n                return Some(monitor.output.clone());\n            }\n        }\n\n        None\n    }\n\n    pub fn view_offset_gesture_begin(\n        &mut self,\n        output: &Output,\n        workspace_idx: Option<usize>,\n        is_touchpad: bool,\n    ) {\n        let monitors = match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => monitors,\n            MonitorSet::NoOutputs { .. } => unreachable!(),\n        };\n\n        for monitor in monitors {\n            for (idx, ws) in monitor.workspaces.iter_mut().enumerate() {\n                // Cancel the gesture on other workspaces.\n                if &monitor.output != output\n                    || idx != workspace_idx.unwrap_or(monitor.active_workspace_idx)\n                {\n                    ws.view_offset_gesture_end(None);\n                    continue;\n                }\n\n                ws.view_offset_gesture_begin(is_touchpad);\n            }\n        }\n    }\n\n    pub fn view_offset_gesture_update(\n        &mut self,\n        delta_x: f64,\n        timestamp: Duration,\n        is_touchpad: bool,\n    ) -> Option<Option<Output>> {\n        let zoom = self.overview_zoom();\n        let delta_x = delta_x / zoom;\n\n        let monitors = match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => monitors,\n            MonitorSet::NoOutputs { .. } => return None,\n        };\n\n        for monitor in monitors {\n            for ws in &mut monitor.workspaces {\n                if let Some(refresh) =\n                    ws.view_offset_gesture_update(delta_x, timestamp, is_touchpad)\n                {\n                    if refresh {\n                        return Some(Some(monitor.output.clone()));\n                    } else {\n                        return Some(None);\n                    }\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn view_offset_gesture_end(&mut self, is_touchpad: Option<bool>) -> Option<Output> {\n        let monitors = match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => monitors,\n            MonitorSet::NoOutputs { .. } => return None,\n        };\n\n        for monitor in monitors {\n            for ws in &mut monitor.workspaces {\n                if ws.view_offset_gesture_end(is_touchpad) {\n                    return Some(monitor.output.clone());\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn overview_gesture_begin(&mut self) {\n        self.overview_open = true;\n\n        let value = self.overview_progress.take().map_or(0., |p| p.value());\n        let gesture = OverviewGesture {\n            tracker: SwipeTracker::new(),\n            start: value,\n            value,\n        };\n        self.overview_progress = Some(OverviewProgress::Gesture(gesture));\n\n        self.set_monitors_overview_state();\n    }\n\n    pub fn overview_gesture_update(&mut self, delta_y: f64, timestamp: Duration) -> Option<bool> {\n        let Some(OverviewProgress::Gesture(gesture)) = &mut self.overview_progress else {\n            return None;\n        };\n\n        gesture.tracker.push(delta_y, timestamp);\n\n        let total_height = OVERVIEW_GESTURE_MOVEMENT;\n        let pos = gesture.tracker.pos() / total_height;\n        let new_value = gesture.start + pos;\n        let new_value = OVERVIEW_GESTURE_RUBBER_BAND.clamp(0., 1., new_value);\n\n        if gesture.value == new_value {\n            return Some(false);\n        }\n\n        gesture.value = new_value;\n        self.set_monitors_overview_state();\n\n        Some(true)\n    }\n\n    pub fn overview_gesture_end(&mut self) -> bool {\n        let Some(OverviewProgress::Gesture(gesture)) = &mut self.overview_progress else {\n            return false;\n        };\n\n        // Take into account any idle time between the last event and now.\n        let now = self.clock.now_unadjusted();\n        gesture.tracker.push(0., now);\n\n        let total_height = OVERVIEW_GESTURE_MOVEMENT;\n\n        let mut velocity = gesture.tracker.velocity() / total_height;\n        let current_pos = gesture.tracker.pos() / total_height;\n        let pos = gesture.tracker.projected_end_pos() / total_height;\n\n        let new_value = gesture.start + pos;\n        let new_value = new_value.clamp(0., 1.).round();\n\n        velocity *=\n            OVERVIEW_GESTURE_RUBBER_BAND.clamp_derivative(0., 1., gesture.start + current_pos);\n\n        self.overview_open = new_value == 1.;\n        self.overview_progress = Some(OverviewProgress::Animation(Animation::new(\n            self.clock.clone(),\n            gesture.value,\n            new_value,\n            velocity,\n            self.options.animations.overview_open_close.0,\n        )));\n\n        self.set_monitors_overview_state();\n\n        true\n    }\n\n    pub fn interactive_move_begin(\n        &mut self,\n        window_id: W::Id,\n        output: &Output,\n        start_pos_within_output: Point<f64, Logical>,\n    ) -> bool {\n        if self.interactive_move.is_some() {\n            return false;\n        }\n\n        let Some((mon, (ws, ws_geo))) = self.monitors().find_map(|mon| {\n            mon.workspaces_with_render_geo()\n                .find(|(ws, _)| ws.has_window(&window_id))\n                .map(|rv| (mon, rv))\n        }) else {\n            return false;\n        };\n\n        if mon.output() != output {\n            return false;\n        }\n\n        let zoom = mon.overview_zoom();\n\n        let is_floating = ws.is_floating(&window_id);\n        let (tile, tile_offset, _visible) = ws\n            .tiles_with_render_positions()\n            .find(|(tile, _, _)| tile.window().id() == &window_id)\n            .unwrap();\n        let window_offset = tile.window_loc();\n\n        let tile_pos = ws_geo.loc + tile_offset.upscale(zoom);\n\n        let pointer_offset_within_window =\n            start_pos_within_output - tile_pos - window_offset.upscale(zoom);\n        let window_size = tile.window_size().upscale(zoom);\n        let pointer_ratio_within_window = (\n            f64::clamp(pointer_offset_within_window.x / window_size.w, 0., 1.),\n            f64::clamp(pointer_offset_within_window.y / window_size.h, 0., 1.),\n        );\n\n        self.interactive_move = Some(InteractiveMoveState::Starting {\n            window_id,\n            pointer_delta: Point::from((0., 0.)),\n            pointer_ratio_within_window,\n        });\n\n        for mon in self.monitors_mut() {\n            mon.dnd_scroll_gesture_begin();\n        }\n\n        // Lock the view for scrolling interactive move.\n        if !is_floating {\n            for ws in self.workspaces_mut() {\n                ws.dnd_scroll_gesture_begin();\n            }\n        }\n\n        true\n    }\n\n    pub fn interactive_move_update(\n        &mut self,\n        window: &W::Id,\n        delta: Point<f64, Logical>,\n        output: Output,\n        pointer_pos_within_output: Point<f64, Logical>,\n    ) -> bool {\n        let Some(state) = self.interactive_move.take() else {\n            return false;\n        };\n\n        match state {\n            InteractiveMoveState::Starting {\n                window_id,\n                mut pointer_delta,\n                pointer_ratio_within_window,\n            } => {\n                if window_id != *window {\n                    self.interactive_move = Some(InteractiveMoveState::Starting {\n                        window_id,\n                        pointer_delta,\n                        pointer_ratio_within_window,\n                    });\n                    return false;\n                }\n\n                let zoom = self.overview_zoom();\n                let delta = delta.downscale(zoom);\n\n                pointer_delta += delta;\n\n                let (cx, cy) = (pointer_delta.x, pointer_delta.y);\n                let sq_dist = cx * cx + cy * cy;\n\n                let factor = RubberBand {\n                    stiffness: 1.0,\n                    limit: 0.5,\n                }\n                .band(sq_dist / INTERACTIVE_MOVE_START_THRESHOLD);\n\n                let (is_floating, tile, workspace_config) = self\n                    .workspaces_mut()\n                    .find(|ws| ws.has_window(&window_id))\n                    .map(|ws| {\n                        let workspace_config = ws.layout_config().cloned().map(|c| (ws.id(), c));\n                        (\n                            ws.is_floating(&window_id),\n                            ws.tiles_mut()\n                                .find(|tile| *tile.window().id() == window_id)\n                                .unwrap(),\n                            workspace_config,\n                        )\n                    })\n                    .unwrap();\n                tile.interactive_move_offset = pointer_delta.upscale(factor);\n\n                // Put it back to be able to easily return.\n                self.interactive_move = Some(InteractiveMoveState::Starting {\n                    window_id: window_id.clone(),\n                    pointer_delta,\n                    pointer_ratio_within_window,\n                });\n\n                if !is_floating && sq_dist < INTERACTIVE_MOVE_START_THRESHOLD {\n                    return true;\n                }\n\n                let output_config = self\n                    .monitors()\n                    .find(|mon| mon.output() == &output)\n                    .and_then(|mon| mon.layout_config().cloned());\n\n                // If the pointer is currently on the window's own output, then we can animate the\n                // window movement from its current (rubberbanded and possibly moved away) position\n                // to the pointer. Otherwise, we just teleport it as the layout code is not aware\n                // of monitor positions.\n                //\n                // FIXME: when and if the layout code knows about monitor positions, this will be\n                // potentially animatable.\n                let mut tile_pos = None;\n                if let Some((mon, (ws, ws_geo))) = self.monitors().find_map(|mon| {\n                    mon.workspaces_with_render_geo()\n                        .find(|(ws, _)| ws.has_window(window))\n                        .map(|rv| (mon, rv))\n                }) {\n                    if mon.output() == &output {\n                        let (_, tile_offset, _) = ws\n                            .tiles_with_render_positions()\n                            .find(|(tile, _, _)| tile.window().id() == window)\n                            .unwrap();\n\n                        let zoom = mon.overview_zoom();\n                        tile_pos = Some((ws_geo.loc + tile_offset.upscale(zoom), zoom));\n                    }\n                }\n\n                // Clear it before calling remove_window() to avoid running interactive_move_end()\n                // in the middle of interactive_move_update() and the confusion that causes.\n                self.interactive_move = None;\n\n                // Unset fullscreen before removing the tile. This will restore its size properly,\n                // and move it to floating if needed, so we don't have to deal with that here.\n                let ws = self\n                    .workspaces_mut()\n                    .find(|ws| ws.has_window(&window_id))\n                    .unwrap();\n                ws.set_fullscreen(window, false);\n                ws.set_maximized(window, false);\n\n                let RemovedTile {\n                    mut tile,\n                    width,\n                    is_full_width,\n                    is_floating,\n                } = self.remove_window(window, Transaction::new()).unwrap();\n\n                tile.stop_move_animations();\n                tile.interactive_move_offset = Point::from((0., 0.));\n                tile.window().output_enter(&output);\n                tile.window().set_preferred_scale_transform(\n                    output.current_scale(),\n                    output.current_transform(),\n                );\n\n                let view_size = output_size(&output);\n                let scale = output.current_scale().fractional_scale();\n                let options = Options::clone(&self.options)\n                    .with_merged_layout(output_config.as_ref())\n                    .with_merged_layout(workspace_config.as_ref().map(|(_, c)| c))\n                    .adjusted_for_scale(scale);\n                tile.update_config(view_size, scale, Rc::new(options));\n\n                if is_floating {\n                    // Unlock the view in case we locked it moving a fullscreen window that is\n                    // going to unfullscreen to floating.\n                    for ws in self.workspaces_mut() {\n                        ws.dnd_scroll_gesture_end();\n                    }\n                } else {\n                    // Animate to semitransparent.\n                    tile.animate_alpha(\n                        1.,\n                        INTERACTIVE_MOVE_ALPHA,\n                        self.options.animations.window_movement.0,\n                    );\n                    tile.hold_alpha_animation_after_done();\n                }\n\n                let mut data = InteractiveMoveData {\n                    tile,\n                    output,\n                    pointer_pos_within_output,\n                    width,\n                    is_full_width,\n                    is_floating,\n                    pointer_ratio_within_window,\n                    output_config,\n                    workspace_config,\n                };\n\n                if let Some((tile_pos, zoom)) = tile_pos {\n                    let new_tile_pos = data.tile_render_location(zoom);\n                    data.tile\n                        .animate_move_from((tile_pos - new_tile_pos).downscale(zoom));\n                }\n\n                self.interactive_move = Some(InteractiveMoveState::Moving(data));\n            }\n            InteractiveMoveState::Moving(mut move_) => {\n                if window != move_.tile.window().id() {\n                    self.interactive_move = Some(InteractiveMoveState::Moving(move_));\n                    return false;\n                }\n\n                let mut ws_id = None;\n                if let Some(mon) = self.monitor_for_output(&output) {\n                    let (insert_ws, _) = mon.insert_position(move_.pointer_pos_within_output);\n                    if let InsertWorkspace::Existing(id) = insert_ws {\n                        ws_id = Some(id);\n                    }\n                }\n\n                // If moved over a different workspace, reset the config override.\n                let mut update_config = false;\n                if let Some((id, _)) = &move_.workspace_config {\n                    if Some(*id) != ws_id {\n                        move_.workspace_config = None;\n                        update_config = true;\n                    }\n                }\n\n                if output != move_.output {\n                    move_.tile.window().output_leave(&move_.output);\n                    move_.tile.window().output_enter(&output);\n                    move_.tile.window().set_preferred_scale_transform(\n                        output.current_scale(),\n                        output.current_transform(),\n                    );\n                    move_.output = output.clone();\n                    self.focus_output(&output);\n\n                    move_.output_config = self\n                        .monitor_for_output(&output)\n                        .and_then(|mon| mon.layout_config().cloned());\n\n                    update_config = true;\n                }\n\n                if update_config {\n                    let view_size = output_size(&output);\n                    let scale = output.current_scale().fractional_scale();\n                    let options = Options::clone(&self.options)\n                        .with_merged_layout(move_.output_config.as_ref())\n                        .with_merged_layout(move_.workspace_config.as_ref().map(|(_, c)| c))\n                        .adjusted_for_scale(scale);\n                    move_.tile.update_config(view_size, scale, Rc::new(options));\n                }\n\n                move_.pointer_pos_within_output = pointer_pos_within_output;\n\n                self.interactive_move = Some(InteractiveMoveState::Moving(move_));\n            }\n        }\n\n        true\n    }\n\n    pub fn interactive_move_end(&mut self, window: &W::Id) {\n        let Some(move_) = &self.interactive_move else {\n            return;\n        };\n\n        let move_ = match move_ {\n            InteractiveMoveState::Starting { window_id, .. } => {\n                if window_id != window {\n                    return;\n                }\n\n                let Some(InteractiveMoveState::Starting { window_id, .. }) =\n                    self.interactive_move.take()\n                else {\n                    unreachable!()\n                };\n\n                for mon in self.monitors_mut() {\n                    mon.dnd_scroll_gesture_end();\n                }\n\n                for ws in self.workspaces_mut() {\n                    if let Some(tile) = ws.tiles_mut().find(|tile| *tile.window().id() == window_id)\n                    {\n                        let offset = tile.interactive_move_offset;\n                        tile.interactive_move_offset = Point::from((0., 0.));\n                        tile.animate_move_from(offset);\n                    }\n\n                    // Unlock the view on the workspaces, but if the moved window was active,\n                    // preserve that.\n                    let moved_tile_was_active =\n                        ws.active_window().is_some_and(|win| *win.id() == window_id);\n\n                    ws.dnd_scroll_gesture_end();\n\n                    if moved_tile_was_active {\n                        ws.activate_window(&window_id);\n                    }\n                }\n\n                return;\n            }\n            InteractiveMoveState::Moving(move_) => move_,\n        };\n\n        if window != move_.tile.window().id() {\n            return;\n        }\n\n        let Some(InteractiveMoveState::Moving(mut move_)) = self.interactive_move.take() else {\n            unreachable!()\n        };\n\n        for mon in self.monitors_mut() {\n            mon.dnd_scroll_gesture_end();\n        }\n\n        // Unlock the view on the workspaces.\n        if !move_.is_floating {\n            for ws in self.workspaces_mut() {\n                ws.dnd_scroll_gesture_end();\n            }\n\n            // Also animate the tile back to opaque.\n            move_.tile.animate_alpha(\n                INTERACTIVE_MOVE_ALPHA,\n                1.,\n                self.options.animations.window_movement.0,\n            );\n        }\n\n        // Dragging in the overview shouldn't switch the workspace and so on.\n        let allow_to_activate_workspace = !self.overview_open;\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal {\n                monitors,\n                active_monitor_idx,\n                ..\n            } => {\n                let (mon, insert_ws, position, offset, zoom) =\n                    if let Some(mon) = monitors.iter_mut().find(|mon| mon.output == move_.output) {\n                        let zoom = mon.overview_zoom();\n\n                        let (insert_ws, geo) = mon.insert_position(move_.pointer_pos_within_output);\n                        let (position, offset) = match insert_ws {\n                            InsertWorkspace::Existing(ws_id) => {\n                                let ws_idx = mon\n                                    .workspaces\n                                    .iter_mut()\n                                    .position(|ws| ws.id() == ws_id)\n                                    .unwrap();\n\n                                let position = if move_.is_floating {\n                                    InsertPosition::Floating\n                                } else {\n                                    let pos_within_workspace =\n                                        (move_.pointer_pos_within_output - geo.loc).downscale(zoom);\n                                    let ws = &mut mon.workspaces[ws_idx];\n                                    ws.scrolling_insert_position(pos_within_workspace)\n                                };\n\n                                (position, Some(geo.loc))\n                            }\n                            InsertWorkspace::NewAt(_) => {\n                                let position = if move_.is_floating {\n                                    InsertPosition::Floating\n                                } else {\n                                    InsertPosition::NewColumn(0)\n                                };\n\n                                (position, None)\n                            }\n                        };\n\n                        (mon, insert_ws, position, offset, zoom)\n                    } else {\n                        let mon = &mut monitors[*active_monitor_idx];\n                        let zoom = mon.overview_zoom();\n                        // No point in trying to use the pointer position on the wrong output.\n                        let ws = &mon.workspaces[0];\n                        let ws_geo = mon.workspaces_render_geo().next().unwrap();\n\n                        let position = if move_.is_floating {\n                            InsertPosition::Floating\n                        } else {\n                            ws.scrolling_insert_position(Point::from((0., 0.)))\n                        };\n\n                        let insert_ws = InsertWorkspace::Existing(ws.id());\n                        (mon, insert_ws, position, Some(ws_geo.loc), zoom)\n                    };\n\n                let win_id = move_.tile.window().id().clone();\n                let tile_render_loc = move_.tile_render_location(zoom);\n\n                let ws_idx = match insert_ws {\n                    InsertWorkspace::Existing(ws_id) => mon\n                        .workspaces\n                        .iter()\n                        .position(|ws| ws.id() == ws_id)\n                        .unwrap(),\n                    InsertWorkspace::NewAt(ws_idx) => {\n                        if mon.options.layout.empty_workspace_above_first && ws_idx == 0 {\n                            // Reuse the top empty workspace.\n                            0\n                        } else if mon.workspaces.len() - 1 <= ws_idx {\n                            // Reuse the bottom empty workspace.\n                            mon.workspaces.len() - 1\n                        } else {\n                            mon.add_workspace_at(ws_idx);\n                            ws_idx\n                        }\n                    }\n                };\n\n                match position {\n                    InsertPosition::NewColumn(column_idx) => {\n                        let ws_id = mon.workspaces[ws_idx].id();\n                        mon.add_tile(\n                            move_.tile,\n                            MonitorAddWindowTarget::Workspace {\n                                id: ws_id,\n                                column_idx: Some(column_idx),\n                            },\n                            ActivateWindow::Yes,\n                            allow_to_activate_workspace,\n                            move_.width,\n                            move_.is_full_width,\n                            false,\n                        );\n                    }\n                    InsertPosition::InColumn(column_idx, tile_idx) => {\n                        mon.add_tile_to_column(\n                            ws_idx,\n                            column_idx,\n                            Some(tile_idx),\n                            move_.tile,\n                            true,\n                            allow_to_activate_workspace,\n                        );\n                    }\n                    InsertPosition::Floating => {\n                        let tile_render_loc = move_.tile_render_location(zoom);\n\n                        let mut tile = move_.tile;\n                        tile.floating_pos = None;\n\n                        match insert_ws {\n                            InsertWorkspace::Existing(_) => {\n                                if let Some(offset) = offset {\n                                    let pos = (tile_render_loc - offset).downscale(zoom);\n                                    let pos =\n                                        mon.workspaces[ws_idx].floating_logical_to_size_frac(pos);\n                                    tile.floating_pos = Some(pos);\n                                } else {\n                                    error!(\n                                        \"offset unset for inserting a floating tile \\\n                                         to existing workspace\"\n                                    );\n                                }\n                            }\n                            InsertWorkspace::NewAt(_) => {\n                                // When putting a floating tile on a new workspace, we don't really\n                                // have a good pre-existing position.\n                            }\n                        }\n\n                        // Set the floating size so it takes into account any window resizing that\n                        // took place during the move.\n                        if let Some(size) = tile.window().expected_size() {\n                            tile.floating_window_size = Some(size);\n                        }\n\n                        let ws_id = mon.workspaces[ws_idx].id();\n                        mon.add_tile(\n                            tile,\n                            MonitorAddWindowTarget::Workspace {\n                                id: ws_id,\n                                column_idx: None,\n                            },\n                            ActivateWindow::Yes,\n                            allow_to_activate_workspace,\n                            move_.width,\n                            move_.is_full_width,\n                            true,\n                        );\n                    }\n                }\n\n                // needed because empty_workspace_above_first could have modified the idx\n                let (tile, tile_offset, ws_geo) = mon\n                    .workspaces_with_render_geo_mut(false)\n                    .find_map(|(ws, geo)| {\n                        ws.tiles_with_render_positions_mut(false)\n                            .find(|(tile, _)| tile.window().id() == &win_id)\n                            .map(|(tile, tile_offset)| (tile, tile_offset, geo))\n                    })\n                    .unwrap();\n                let new_tile_render_loc = ws_geo.loc + tile_offset.upscale(zoom);\n\n                tile.animate_move_from((tile_render_loc - new_tile_render_loc).downscale(zoom));\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                if workspaces.is_empty() {\n                    workspaces.push(Workspace::new_no_outputs(\n                        self.clock.clone(),\n                        self.options.clone(),\n                    ));\n                }\n                let ws = &mut workspaces[0];\n\n                // No point in trying to use the pointer position without outputs.\n                ws.add_tile(\n                    move_.tile,\n                    WorkspaceAddWindowTarget::Auto,\n                    ActivateWindow::Yes,\n                    move_.width,\n                    move_.is_full_width,\n                    move_.is_floating,\n                );\n            }\n        }\n    }\n\n    pub fn interactive_move_is_moving_above_output(&self, output: &Output) -> bool {\n        let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move else {\n            return false;\n        };\n\n        move_.output == *output\n    }\n\n    pub fn dnd_update(&mut self, output: Output, pointer_pos_within_output: Point<f64, Logical>) {\n        let begin_gesture = self.dnd.is_none();\n\n        self.dnd = Some(DndData {\n            output,\n            pointer_pos_within_output,\n            hold: None,\n        });\n\n        if begin_gesture {\n            for mon in self.monitors_mut() {\n                mon.dnd_scroll_gesture_begin();\n            }\n\n            for ws in self.workspaces_mut() {\n                ws.dnd_scroll_gesture_begin();\n            }\n        }\n    }\n\n    pub fn dnd_end(&mut self) {\n        if self.dnd.is_none() {\n            return;\n        }\n\n        self.dnd = None;\n\n        for mon in self.monitors_mut() {\n            mon.dnd_scroll_gesture_end();\n        }\n\n        for ws in self.workspaces_mut() {\n            ws.dnd_scroll_gesture_end();\n        }\n    }\n\n    pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if ws.has_window(&window) {\n                            return ws.interactive_resize_begin(window, edges);\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    if ws.has_window(&window) {\n                        return ws.interactive_resize_begin(window, edges);\n                    }\n                }\n            }\n        }\n\n        false\n    }\n\n    pub fn interactive_resize_update(\n        &mut self,\n        window: &W::Id,\n        delta: Point<f64, Logical>,\n    ) -> bool {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                return false;\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if ws.has_window(window) {\n                            return ws.interactive_resize_update(window, delta);\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    if ws.has_window(window) {\n                        return ws.interactive_resize_update(window, delta);\n                    }\n                }\n            }\n        }\n\n        false\n    }\n\n    pub fn interactive_resize_end(&mut self, window: &W::Id) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                return;\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if ws.has_window(window) {\n                            ws.interactive_resize_end(Some(window));\n                            return;\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    if ws.has_window(window) {\n                        ws.interactive_resize_end(Some(window));\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn move_workspace_down(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_workspace_down();\n    }\n\n    pub fn move_workspace_up(&mut self) {\n        let Some(monitor) = self.active_monitor() else {\n            return;\n        };\n        monitor.move_workspace_up();\n    }\n\n    pub fn move_workspace_to_idx(\n        &mut self,\n        reference: Option<(Option<Output>, usize)>,\n        new_idx: usize,\n    ) {\n        let (monitor, old_idx) = if let Some((output, old_idx)) = reference {\n            let monitor = if let Some(output) = output {\n                let Some(monitor) = self.monitor_for_output_mut(&output) else {\n                    return;\n                };\n                monitor\n            } else {\n                // In case a numbered workspace reference is used, assume the active monitor\n                let Some(monitor) = self.active_monitor() else {\n                    return;\n                };\n                monitor\n            };\n\n            (monitor, old_idx)\n        } else {\n            let Some(monitor) = self.active_monitor() else {\n                return;\n            };\n            let index = monitor.active_workspace_idx;\n            (monitor, index)\n        };\n\n        monitor.move_workspace_to_idx(old_idx, new_idx);\n    }\n\n    pub fn set_workspace_name(&mut self, name: String, reference: Option<WorkspaceReference>) {\n        // ignore the request if the name is already used by another workspace\n        if self.find_workspace_by_name(&name).is_some() {\n            return;\n        }\n\n        let ws = if let Some(reference) = reference {\n            self.find_workspace_by_ref(reference)\n        } else {\n            self.active_workspace_mut()\n        };\n        let Some(ws) = ws else {\n            return;\n        };\n\n        ws.name.replace(name);\n\n        let wsid = ws.id();\n\n        // if `empty_workspace_above_first` is set and `ws` is the first\n        // workspace on a monitor, another empty workspace needs to\n        // be added before.\n        // Conversely, if `ws` was the last workspace on a monitor, an\n        // empty workspace needs to be added after.\n\n        if let MonitorSet::Normal {\n            monitors,\n            active_monitor_idx,\n            ..\n        } = &mut self.monitor_set\n        {\n            let monitor = &mut monitors[*active_monitor_idx];\n            if monitor.options.layout.empty_workspace_above_first\n                && monitor\n                    .workspaces\n                    .first()\n                    .is_some_and(|first| first.id() == wsid)\n            {\n                monitor.add_workspace_top();\n            }\n            if monitor\n                .workspaces\n                .last()\n                .is_some_and(|last| last.id() == wsid)\n            {\n                monitor.add_workspace_bottom();\n            }\n        }\n    }\n\n    pub fn unset_workspace_name(&mut self, reference: Option<WorkspaceReference>) {\n        let ws = if let Some(reference) = reference {\n            self.find_workspace_by_ref(reference)\n        } else {\n            self.active_workspace_mut()\n        };\n        let Some(ws) = ws else {\n            return;\n        };\n        let id = ws.id();\n\n        self.unname_workspace_by_id(id);\n    }\n\n    pub fn set_monitors_overview_state(&mut self) {\n        let MonitorSet::Normal { monitors, .. } = &mut self.monitor_set else {\n            return;\n        };\n\n        for mon in monitors {\n            mon.overview_open = self.overview_open;\n            mon.set_overview_progress(self.overview_progress.as_ref());\n        }\n    }\n\n    pub fn toggle_overview(&mut self) {\n        self.overview_open = !self.overview_open;\n\n        let from = self.overview_progress.take().map_or(0., |p| p.value());\n        let to = if self.overview_open { 1. } else { 0. };\n\n        self.overview_progress = Some(OverviewProgress::Animation(Animation::new(\n            self.clock.clone(),\n            from,\n            to,\n            0.,\n            self.options.animations.overview_open_close.0,\n        )));\n\n        self.set_monitors_overview_state();\n    }\n\n    pub fn open_overview(&mut self) -> bool {\n        if self.overview_open {\n            return false;\n        }\n\n        self.toggle_overview();\n        true\n    }\n\n    pub fn close_overview(&mut self) -> bool {\n        if !self.overview_open {\n            return false;\n        }\n\n        self.toggle_overview();\n        true\n    }\n\n    pub fn toggle_overview_to_workspace(&mut self, ws_idx: usize) {\n        let config = self.options.animations.overview_open_close.0;\n        if let Some(mon) = self.active_monitor() {\n            mon.activate_workspace_with_anim_config(ws_idx, Some(config));\n        }\n        self.toggle_overview();\n    }\n\n    pub fn start_open_animation_for_window(&mut self, window: &W::Id) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {\n            if move_.tile.window().id() == window {\n                return;\n            }\n        }\n\n        for ws in self.workspaces_mut() {\n            if ws.start_open_animation(window) {\n                return;\n            }\n        }\n    }\n\n    pub fn store_unmap_snapshot(&mut self, renderer: &mut GlesRenderer, window: &W::Id) {\n        let _span = tracy_client::span!(\"Layout::store_unmap_snapshot\");\n\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if move_.tile.window().id() == window {\n                move_.tile.store_unmap_snapshot_if_empty(renderer);\n                return;\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if ws.has_window(window) {\n                            ws.store_unmap_snapshot_if_empty(renderer, window);\n                            return;\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    if ws.has_window(window) {\n                        ws.store_unmap_snapshot_if_empty(renderer, window);\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn clear_unmap_snapshot(&mut self, window: &W::Id) {\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if move_.tile.window().id() == window {\n                let _ = move_.tile.take_unmap_snapshot();\n                return;\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if ws.has_window(window) {\n                            ws.clear_unmap_snapshot(window);\n                            return;\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    if ws.has_window(window) {\n                        ws.clear_unmap_snapshot(window);\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn start_close_animation_for_window(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        window: &W::Id,\n        blocker: TransactionBlocker,\n    ) {\n        let _span = tracy_client::span!(\"Layout::start_close_animation_for_window\");\n\n        let zoom = self.overview_zoom();\n\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            if move_.tile.window().id() == window {\n                let Some(snapshot) = move_.tile.take_unmap_snapshot() else {\n                    return;\n                };\n                let tile_pos = move_.tile_render_location(zoom);\n                let tile_size = move_.tile.tile_size();\n\n                let output = move_.output.clone();\n                let pointer_pos_within_output = move_.pointer_pos_within_output;\n                let Some(mon) = self.monitor_for_output_mut(&output) else {\n                    return;\n                };\n                let Some((ws, ws_geo)) = mon.workspace_under(pointer_pos_within_output) else {\n                    return;\n                };\n                let ws_id = ws.id();\n                let ws = mon\n                    .workspaces\n                    .iter_mut()\n                    .find(|ws| ws.id() == ws_id)\n                    .unwrap();\n\n                let tile_pos = tile_pos - ws_geo.loc;\n                ws.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);\n                return;\n            }\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                for mon in monitors {\n                    for ws in &mut mon.workspaces {\n                        if ws.has_window(window) {\n                            ws.start_close_animation_for_window(renderer, window, blocker);\n                            return;\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    if ws.has_window(window) {\n                        ws.start_close_animation_for_window(renderer, window, blocker);\n                        return;\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn render_interactive_move_for_output<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        output: &Output,\n        target: RenderTarget,\n        push: &mut dyn FnMut(RescaleRenderElement<TileRenderElement<R>>),\n    ) {\n        if self.update_render_elements_time != self.clock.now() {\n            error!(\"clock moved between updating render elements and rendering\");\n        }\n\n        let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move else {\n            return;\n        };\n\n        if &move_.output != output {\n            return;\n        }\n\n        let scale = Scale::from(move_.output.current_scale().fractional_scale());\n        let zoom = self.overview_zoom();\n        let location = move_.tile_render_location(zoom);\n        move_\n            .tile\n            .render(renderer, location, true, target, &mut |elem| {\n                push(RescaleRenderElement::from_element(\n                    elem,\n                    location.to_physical_precise_round(scale),\n                    zoom,\n                ));\n            });\n    }\n\n    pub fn refresh(&mut self, is_active: bool) {\n        let _span = tracy_client::span!(\"Layout::refresh\");\n\n        self.is_active = is_active;\n\n        let mut ongoing_scrolling_dnd = self.dnd.is_some().then_some(true);\n\n        if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {\n            let win = move_.tile.window_mut();\n\n            win.set_active_in_column(true);\n            win.set_floating(move_.is_floating);\n            win.set_activated(true);\n\n            win.set_interactive_resize(None);\n\n            win.set_bounds(output_size(&move_.output).to_i32_round());\n\n            win.send_pending_configure();\n            win.refresh();\n\n            ongoing_scrolling_dnd.get_or_insert(!move_.is_floating);\n        } else if let Some(InteractiveMoveState::Starting { window_id, .. }) =\n            &self.interactive_move\n        {\n            ongoing_scrolling_dnd.get_or_insert_with(|| {\n                let (_, _, ws) = self\n                    .workspaces()\n                    .find(|(_, _, ws)| ws.has_window(window_id))\n                    .unwrap();\n                !ws.is_floating(window_id)\n            });\n        }\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal {\n                monitors,\n                active_monitor_idx,\n                ..\n            } => {\n                for (idx, mon) in monitors.iter_mut().enumerate() {\n                    let is_active = self.is_active\n                        && idx == *active_monitor_idx\n                        && !matches!(self.interactive_move, Some(InteractiveMoveState::Moving(_)));\n\n                    if ongoing_scrolling_dnd.is_some() && self.overview_open {\n                        // Begin the scroll on new monitors and when opening the overview.\n                        mon.dnd_scroll_gesture_begin();\n                    } else if !self.overview_open {\n                        mon.dnd_scroll_gesture_end();\n                    }\n\n                    for (ws_idx, ws) in mon.workspaces.iter_mut().enumerate() {\n                        let is_focused = is_active && ws_idx == mon.active_workspace_idx;\n                        ws.refresh(is_active, is_focused);\n\n                        if let Some(is_scrolling) = ongoing_scrolling_dnd {\n                            // Lock or unlock the view for scrolling interactive move.\n                            if is_scrolling {\n                                ws.dnd_scroll_gesture_begin();\n                            } else {\n                                ws.dnd_scroll_gesture_end();\n                            }\n                        } else {\n                            // Cancel the view offset gesture after workspace switches, moves, etc.\n                            if !self.overview_open && ws_idx != mon.active_workspace_idx {\n                                ws.view_offset_gesture_end(None);\n                            }\n                        }\n                    }\n                }\n            }\n            MonitorSet::NoOutputs { workspaces, .. } => {\n                for ws in workspaces {\n                    ws.refresh(false, false);\n                    ws.view_offset_gesture_end(None);\n                }\n            }\n        }\n    }\n\n    pub fn workspaces(\n        &self,\n    ) -> impl Iterator<Item = (Option<&Monitor<W>>, usize, &Workspace<W>)> + '_ {\n        let iter_normal;\n        let iter_no_outputs;\n\n        match &self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                let it = monitors.iter().flat_map(|mon| {\n                    mon.workspaces\n                        .iter()\n                        .enumerate()\n                        .map(move |(idx, ws)| (Some(mon), idx, ws))\n                });\n\n                iter_normal = Some(it);\n                iter_no_outputs = None;\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                let it = workspaces\n                    .iter()\n                    .enumerate()\n                    .map(|(idx, ws)| (None, idx, ws));\n\n                iter_normal = None;\n                iter_no_outputs = Some(it);\n            }\n        }\n\n        let iter_normal = iter_normal.into_iter().flatten();\n        let iter_no_outputs = iter_no_outputs.into_iter().flatten();\n        iter_normal.chain(iter_no_outputs)\n    }\n\n    pub fn workspaces_mut(&mut self) -> impl Iterator<Item = &mut Workspace<W>> + '_ {\n        let iter_normal;\n        let iter_no_outputs;\n\n        match &mut self.monitor_set {\n            MonitorSet::Normal { monitors, .. } => {\n                let it = monitors\n                    .iter_mut()\n                    .flat_map(|mon| mon.workspaces.iter_mut());\n\n                iter_normal = Some(it);\n                iter_no_outputs = None;\n            }\n            MonitorSet::NoOutputs { workspaces } => {\n                let it = workspaces.iter_mut();\n\n                iter_normal = None;\n                iter_no_outputs = Some(it);\n            }\n        }\n\n        let iter_normal = iter_normal.into_iter().flatten();\n        let iter_no_outputs = iter_no_outputs.into_iter().flatten();\n        iter_normal.chain(iter_no_outputs)\n    }\n\n    pub fn windows(&self) -> impl Iterator<Item = (Option<&Monitor<W>>, &W)> {\n        let moving_window = self\n            .interactive_move\n            .as_ref()\n            .and_then(|x| x.moving())\n            .map(|move_| (self.monitor_for_output(&move_.output), move_.tile.window()))\n            .into_iter();\n\n        let rest = self\n            .workspaces()\n            .flat_map(|(mon, _, ws)| ws.windows().map(move |win| (mon, win)));\n\n        moving_window.chain(rest)\n    }\n\n    pub fn has_window(&self, window: &W::Id) -> bool {\n        self.windows().any(|(_, win)| win.id() == window)\n    }\n\n    pub fn is_overview_open(&self) -> bool {\n        self.overview_open\n    }\n}\n\nimpl<W: LayoutElement> Default for MonitorSet<W> {\n    fn default() -> Self {\n        Self::NoOutputs { workspaces: vec![] }\n    }\n}\n\nfn compute_overview_zoom(options: &Options, overview_progress: Option<f64>) -> f64 {\n    // Clamp to some sane values.\n    let zoom = options.overview.zoom.clamp(0.0001, 0.75);\n\n    if let Some(p) = overview_progress {\n        (1. - p * (1. - zoom)).max(0.0001)\n    } else {\n        1.\n    }\n}\n"
  },
  {
    "path": "src/layout/monitor.rs",
    "content": "use std::cmp::min;\nuse std::iter::zip;\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse niri_config::{CornerRadius, LayoutPart};\nuse smithay::backend::renderer::element::utils::{\n    CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement,\n};\nuse smithay::output::Output;\nuse smithay::utils::{Logical, Point, Rectangle, Size};\n\nuse super::insert_hint_element::{InsertHintElement, InsertHintRenderElement};\nuse super::scrolling::{Column, ColumnWidth};\nuse super::tile::Tile;\nuse super::workspace::{\n    compute_working_area, OutputId, Workspace, WorkspaceAddWindowTarget, WorkspaceId,\n    WorkspaceRenderElement,\n};\nuse super::{compute_overview_zoom, ActivateWindow, HitType, LayoutElement, Options};\nuse crate::animation::{Animation, Clock};\nuse crate::input::swipe_tracker::SwipeTracker;\nuse crate::niri_render_elements;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::shadow::ShadowRenderElement;\nuse crate::render_helpers::solid_color::SolidColorRenderElement;\nuse crate::render_helpers::RenderTarget;\nuse crate::rubber_band::RubberBand;\nuse crate::utils::transaction::Transaction;\nuse crate::utils::{\n    output_size, round_logical_in_physical, round_logical_in_physical_max1, ResizeEdge,\n};\n\n/// Amount of touchpad movement to scroll the height of one workspace.\nconst WORKSPACE_GESTURE_MOVEMENT: f64 = 300.;\n\nconst WORKSPACE_GESTURE_RUBBER_BAND: RubberBand = RubberBand {\n    stiffness: 0.5,\n    limit: 0.05,\n};\n\n/// Amount of DnD edge scrolling to scroll the height of one workspace.\n///\n/// This constant is tied to the default dnd-edge-workspace-switch max-speed setting.\nconst WORKSPACE_DND_EDGE_SCROLL_MOVEMENT: f64 = 1500.;\n\n#[derive(Debug)]\npub struct Monitor<W: LayoutElement> {\n    /// Output for this monitor.\n    pub(super) output: Output,\n    /// Cached name of the output.\n    output_name: String,\n    /// Latest known scale for this output.\n    scale: smithay::output::Scale,\n    /// Latest known size for this output.\n    view_size: Size<f64, Logical>,\n    /// Latest known working area for this output.\n    ///\n    /// Not rounded to physical pixels.\n    // FIXME: since this is used for things like DnD scrolling edges in the overview, ideally this\n    // should only consider overlay and top layer-shell surfaces. However, Smithay doesn't easily\n    // let you do this at the moment.\n    working_area: Rectangle<f64, Logical>,\n    // Must always contain at least one.\n    pub(super) workspaces: Vec<Workspace<W>>,\n    /// Index of the currently active workspace.\n    pub(super) active_workspace_idx: usize,\n    /// ID of the previously active workspace.\n    pub(super) previous_workspace_id: Option<WorkspaceId>,\n    /// In-progress switch between workspaces.\n    pub(super) workspace_switch: Option<WorkspaceSwitch>,\n    /// Indication where an interactively-moved window is about to be placed.\n    pub(super) insert_hint: Option<InsertHint>,\n    /// Insert hint element for rendering.\n    insert_hint_element: InsertHintElement,\n    /// Location to render the insert hint element.\n    insert_hint_render_loc: Option<InsertHintRenderLoc>,\n    /// Whether the overview is open.\n    pub(super) overview_open: bool,\n    /// Progress of the overview zoom animation, 1 is fully in overview.\n    overview_progress: Option<OverviewProgress>,\n    /// Clock for driving animations.\n    pub(super) clock: Clock,\n    /// Configurable properties of the layout as received from the parent layout.\n    pub(super) base_options: Rc<Options>,\n    /// Configurable properties of the layout.\n    pub(super) options: Rc<Options>,\n    /// Layout config overrides for this monitor.\n    layout_config: Option<niri_config::LayoutPart>,\n}\n\n#[derive(Debug)]\npub enum WorkspaceSwitch {\n    Animation(Animation),\n    Gesture(WorkspaceSwitchGesture),\n}\n\n#[derive(Debug)]\npub struct WorkspaceSwitchGesture {\n    /// Index of the workspace where the gesture was started.\n    center_idx: usize,\n    /// Fractional workspace index where the gesture was started.\n    ///\n    /// Can differ from center_idx when starting a gesture in the middle between workspaces, for\n    /// example by \"catching\" an animation.\n    start_idx: f64,\n    /// Current, fractional workspace index.\n    pub(super) current_idx: f64,\n    /// Animation for the extra offset to the current position.\n    ///\n    /// For example, if there's a workspace switch during a DnD scroll.\n    animation: Option<Animation>,\n    tracker: SwipeTracker,\n    /// Whether the gesture is controlled by the touchpad.\n    is_touchpad: bool,\n    /// Whether the gesture is clamped to +-1 workspace around the center.\n    is_clamped: bool,\n\n    // If this gesture is for drag-and-drop scrolling, this is the last event's unadjusted\n    // timestamp.\n    dnd_last_event_time: Option<Duration>,\n    // Time when the drag-and-drop scroll delta became non-zero, used for debouncing.\n    //\n    // If `None` then the scroll delta is currently zero.\n    dnd_nonzero_start_time: Option<Duration>,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub(super) enum InsertPosition {\n    NewColumn(usize),\n    InColumn(usize, usize),\n    Floating,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub(super) enum InsertWorkspace {\n    Existing(WorkspaceId),\n    NewAt(usize),\n}\n\n#[derive(Debug)]\npub(super) struct InsertHint {\n    pub workspace: InsertWorkspace,\n    pub position: InsertPosition,\n    pub corner_radius: CornerRadius,\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct InsertHintRenderLoc {\n    workspace: InsertWorkspace,\n    location: Point<f64, Logical>,\n}\n\n#[derive(Debug)]\npub(super) enum OverviewProgress {\n    Animation(Animation),\n    Value(f64),\n}\n\n/// Where to put a newly added window.\n#[derive(Debug, Default, PartialEq, Eq)]\npub enum MonitorAddWindowTarget<'a, W: LayoutElement> {\n    /// No particular preference.\n    #[default]\n    Auto,\n    /// On this workspace.\n    Workspace {\n        /// Id of the target workspace.\n        id: WorkspaceId,\n        /// Override where the window will open as a new column.\n        column_idx: Option<usize>,\n    },\n    /// Next to this existing window.\n    NextTo(&'a W::Id),\n}\n\nimpl<'a, W: LayoutElement> Copy for MonitorAddWindowTarget<'a, W> {}\n\nimpl<'a, W: LayoutElement> Clone for MonitorAddWindowTarget<'a, W> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nniri_render_elements! {\n    MonitorInnerRenderElement<R> => {\n        Workspace = CropRenderElement<WorkspaceRenderElement<R>>,\n        InsertHint = CropRenderElement<InsertHintRenderElement>,\n        UncroppedInsertHint = InsertHintRenderElement,\n        Shadow = ShadowRenderElement,\n        SolidColor = SolidColorRenderElement,\n    }\n}\n\npub type MonitorRenderElement<R> =\n    RelocateRenderElement<RescaleRenderElement<MonitorInnerRenderElement<R>>>;\n\nimpl WorkspaceSwitch {\n    pub fn current_idx(&self) -> f64 {\n        match self {\n            WorkspaceSwitch::Animation(anim) => anim.value(),\n            WorkspaceSwitch::Gesture(gesture) => {\n                gesture.current_idx + gesture.animation.as_ref().map_or(0., |anim| anim.value())\n            }\n        }\n    }\n\n    pub fn target_idx(&self) -> f64 {\n        match self {\n            WorkspaceSwitch::Animation(anim) => anim.to(),\n            WorkspaceSwitch::Gesture(gesture) => gesture.current_idx,\n        }\n    }\n\n    pub fn offset(&mut self, delta: isize) {\n        match self {\n            WorkspaceSwitch::Animation(anim) => anim.offset(delta as f64),\n            WorkspaceSwitch::Gesture(gesture) => {\n                if delta >= 0 {\n                    gesture.center_idx += delta as usize;\n                } else {\n                    gesture.center_idx -= (-delta) as usize;\n                }\n                gesture.start_idx += delta as f64;\n                gesture.current_idx += delta as f64;\n            }\n        }\n    }\n\n    fn is_animation_ongoing(&self) -> bool {\n        match self {\n            WorkspaceSwitch::Animation(_) => true,\n            WorkspaceSwitch::Gesture(gesture) => gesture.animation.is_some(),\n        }\n    }\n}\n\nimpl WorkspaceSwitchGesture {\n    fn min_max(&self, workspace_count: usize) -> (f64, f64) {\n        if self.is_clamped {\n            let min = self.center_idx.saturating_sub(1) as f64;\n            let max = (self.center_idx + 1).min(workspace_count - 1) as f64;\n            (min, max)\n        } else {\n            (0., (workspace_count - 1) as f64)\n        }\n    }\n\n    fn animate_from(&mut self, from: f64, clock: Clock, config: niri_config::Animation) {\n        let current = self.animation.as_ref().map_or(0., Animation::value);\n        self.animation = Some(Animation::new(clock, from + current, 0., 0., config));\n    }\n}\n\nimpl InsertWorkspace {\n    fn existing_id(self) -> Option<WorkspaceId> {\n        match self {\n            InsertWorkspace::Existing(id) => Some(id),\n            InsertWorkspace::NewAt(_) => None,\n        }\n    }\n}\n\nimpl OverviewProgress {\n    pub fn value(&self) -> f64 {\n        match self {\n            OverviewProgress::Animation(anim) => anim.value(),\n            OverviewProgress::Value(v) => *v,\n        }\n    }\n\n    pub fn clamped_value(&self) -> f64 {\n        match self {\n            OverviewProgress::Animation(anim) => anim.clamped_value(),\n            OverviewProgress::Value(v) => *v,\n        }\n    }\n}\n\nimpl From<&super::OverviewProgress> for OverviewProgress {\n    fn from(value: &super::OverviewProgress) -> Self {\n        match value {\n            super::OverviewProgress::Animation(anim) => Self::Animation(anim.clone()),\n            super::OverviewProgress::Gesture(gesture) => Self::Value(gesture.value),\n            super::OverviewProgress::Open => Self::Value(1.),\n        }\n    }\n}\n\nimpl<W: LayoutElement> Monitor<W> {\n    pub fn new(\n        output: Output,\n        mut workspaces: Vec<Workspace<W>>,\n        ws_id_to_activate: Option<WorkspaceId>,\n        clock: Clock,\n        base_options: Rc<Options>,\n        layout_config: Option<LayoutPart>,\n    ) -> Self {\n        let options =\n            Rc::new(Options::clone(&base_options).with_merged_layout(layout_config.as_ref()));\n\n        let scale = output.current_scale();\n        let view_size = output_size(&output);\n        let working_area = compute_working_area(&output);\n\n        // Prepare the workspaces: set output, empty first, empty last.\n        let mut active_workspace_idx = 0;\n\n        for (idx, ws) in workspaces.iter_mut().enumerate() {\n            assert!(ws.has_windows_or_name());\n\n            ws.set_output(Some(output.clone()));\n            ws.update_config(options.clone());\n\n            if ws_id_to_activate.is_some_and(|id| ws.id() == id) {\n                active_workspace_idx = idx;\n            }\n        }\n\n        if options.layout.empty_workspace_above_first && !workspaces.is_empty() {\n            let ws = Workspace::new(output.clone(), clock.clone(), options.clone());\n            workspaces.insert(0, ws);\n            active_workspace_idx += 1;\n        }\n\n        let ws = Workspace::new(output.clone(), clock.clone(), options.clone());\n        workspaces.push(ws);\n\n        Self {\n            output_name: output.name(),\n            output,\n            scale,\n            view_size,\n            working_area,\n            workspaces,\n            active_workspace_idx,\n            previous_workspace_id: None,\n            insert_hint: None,\n            insert_hint_element: InsertHintElement::new(options.layout.insert_hint),\n            insert_hint_render_loc: None,\n            overview_open: false,\n            overview_progress: None,\n            workspace_switch: None,\n            clock,\n            base_options,\n            options,\n            layout_config,\n        }\n    }\n\n    pub fn into_workspaces(mut self) -> Vec<Workspace<W>> {\n        self.workspaces.retain(|ws| ws.has_windows_or_name());\n\n        for ws in &mut self.workspaces {\n            ws.set_output(None);\n        }\n\n        self.workspaces\n    }\n\n    pub fn output(&self) -> &Output {\n        &self.output\n    }\n\n    pub fn output_name(&self) -> &String {\n        &self.output_name\n    }\n\n    pub fn active_workspace_idx(&self) -> usize {\n        self.active_workspace_idx\n    }\n\n    pub fn active_workspace_ref(&self) -> &Workspace<W> {\n        &self.workspaces[self.active_workspace_idx]\n    }\n\n    pub fn find_named_workspace(&self, workspace_name: &str) -> Option<&Workspace<W>> {\n        self.workspaces.iter().find(|ws| {\n            ws.name\n                .as_ref()\n                .is_some_and(|name| name.eq_ignore_ascii_case(workspace_name))\n        })\n    }\n\n    pub fn find_named_workspace_index(&self, workspace_name: &str) -> Option<usize> {\n        self.workspaces.iter().position(|ws| {\n            ws.name\n                .as_ref()\n                .is_some_and(|name| name.eq_ignore_ascii_case(workspace_name))\n        })\n    }\n\n    pub fn active_workspace(&mut self) -> &mut Workspace<W> {\n        &mut self.workspaces[self.active_workspace_idx]\n    }\n\n    pub fn windows(&self) -> impl Iterator<Item = &W> {\n        self.workspaces.iter().flat_map(|ws| ws.windows())\n    }\n\n    pub fn has_window(&self, window: &W::Id) -> bool {\n        self.windows().any(|win| win.id() == window)\n    }\n\n    pub fn add_workspace_at(&mut self, idx: usize) {\n        let ws = Workspace::new(\n            self.output.clone(),\n            self.clock.clone(),\n            self.options.clone(),\n        );\n\n        self.workspaces.insert(idx, ws);\n        if idx <= self.active_workspace_idx {\n            self.active_workspace_idx += 1;\n        }\n\n        if let Some(switch) = &mut self.workspace_switch {\n            if idx as f64 <= switch.target_idx() {\n                switch.offset(1);\n            }\n        }\n    }\n\n    pub fn add_workspace_top(&mut self) {\n        self.add_workspace_at(0);\n    }\n\n    pub fn add_workspace_bottom(&mut self) {\n        self.add_workspace_at(self.workspaces.len());\n    }\n\n    pub fn activate_workspace(&mut self, idx: usize) {\n        self.activate_workspace_with_anim_config(idx, None);\n    }\n\n    pub fn activate_workspace_with_anim_config(\n        &mut self,\n        idx: usize,\n        config: Option<niri_config::Animation>,\n    ) {\n        // FIXME: also compute and use current velocity.\n        let current_idx = self.workspace_render_idx();\n\n        if self.active_workspace_idx != idx {\n            self.previous_workspace_id = Some(self.workspaces[self.active_workspace_idx].id());\n        }\n\n        let prev_active_idx = self.active_workspace_idx;\n        self.active_workspace_idx = idx;\n\n        let config = config.unwrap_or(self.options.animations.workspace_switch.0);\n\n        match &mut self.workspace_switch {\n            // During a DnD scroll, we want to visually animate even if idx matches the active idx.\n            Some(WorkspaceSwitch::Gesture(gesture)) if gesture.dnd_last_event_time.is_some() => {\n                gesture.center_idx = idx;\n\n                // Adjust start_idx to make current_idx point at idx.\n                let current_pos = gesture.current_idx - gesture.start_idx;\n                gesture.start_idx = idx as f64 - current_pos;\n                let prev_current_idx = gesture.current_idx;\n                gesture.current_idx = idx as f64;\n\n                let current_idx_delta = gesture.current_idx - prev_current_idx;\n                gesture.animate_from(-current_idx_delta, self.clock.clone(), config);\n            }\n            _ => {\n                // Don't animate if nothing changed.\n                if prev_active_idx == idx {\n                    return;\n                }\n\n                self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new(\n                    self.clock.clone(),\n                    current_idx,\n                    idx as f64,\n                    0.,\n                    config,\n                )));\n            }\n        }\n    }\n\n    pub(super) fn resolve_add_window_target<'a>(\n        &mut self,\n        target: MonitorAddWindowTarget<'a, W>,\n    ) -> (usize, WorkspaceAddWindowTarget<'a, W>) {\n        match target {\n            MonitorAddWindowTarget::Auto => {\n                (self.active_workspace_idx, WorkspaceAddWindowTarget::Auto)\n            }\n            MonitorAddWindowTarget::Workspace { id, column_idx } => {\n                let idx = self.workspaces.iter().position(|ws| ws.id() == id).unwrap();\n                let target = if let Some(column_idx) = column_idx {\n                    WorkspaceAddWindowTarget::NewColumnAt(column_idx)\n                } else {\n                    WorkspaceAddWindowTarget::Auto\n                };\n                (idx, target)\n            }\n            MonitorAddWindowTarget::NextTo(win_id) => {\n                let idx = self\n                    .workspaces\n                    .iter_mut()\n                    .position(|ws| ws.has_window(win_id))\n                    .unwrap();\n                (idx, WorkspaceAddWindowTarget::NextTo(win_id))\n            }\n        }\n    }\n\n    pub fn add_window(\n        &mut self,\n        window: W,\n        target: MonitorAddWindowTarget<W>,\n        activate: ActivateWindow,\n        width: ColumnWidth,\n        is_full_width: bool,\n        is_floating: bool,\n    ) {\n        // Currently, everything a workspace sets on a Tile is the same across all workspaces of a\n        // monitor. So we can use any workspace, not necessarily the exact target workspace.\n        let tile = self.workspaces[0].make_tile(window);\n\n        self.add_tile(\n            tile,\n            target,\n            activate,\n            true,\n            width,\n            is_full_width,\n            is_floating,\n        );\n    }\n\n    pub fn add_column(&mut self, mut workspace_idx: usize, column: Column<W>, activate: bool) {\n        let workspace = &mut self.workspaces[workspace_idx];\n\n        workspace.add_column(column, activate);\n\n        // After adding a new window, workspace becomes this output's own.\n        if workspace.name().is_none() {\n            workspace.original_output = OutputId::new(&self.output);\n        }\n\n        if workspace_idx == self.workspaces.len() - 1 {\n            self.add_workspace_bottom();\n        }\n        if self.options.layout.empty_workspace_above_first && workspace_idx == 0 {\n            self.add_workspace_top();\n            workspace_idx += 1;\n        }\n\n        if activate {\n            self.activate_workspace(workspace_idx);\n        }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn add_tile(\n        &mut self,\n        tile: Tile<W>,\n        target: MonitorAddWindowTarget<W>,\n        activate: ActivateWindow,\n        // FIXME: Refactor ActivateWindow enum to make this better.\n        allow_to_activate_workspace: bool,\n        width: ColumnWidth,\n        is_full_width: bool,\n        is_floating: bool,\n    ) {\n        let (mut workspace_idx, target) = self.resolve_add_window_target(target);\n\n        let workspace = &mut self.workspaces[workspace_idx];\n\n        workspace.add_tile(tile, target, activate, width, is_full_width, is_floating);\n\n        // After adding a new window, workspace becomes this output's own.\n        if workspace.name().is_none() {\n            workspace.original_output = OutputId::new(&self.output);\n        }\n\n        if workspace_idx == self.workspaces.len() - 1 {\n            // Insert a new empty workspace.\n            self.add_workspace_bottom();\n        }\n\n        if self.options.layout.empty_workspace_above_first && workspace_idx == 0 {\n            self.add_workspace_top();\n            workspace_idx += 1;\n        }\n\n        if allow_to_activate_workspace && activate.map_smart(|| false) {\n            self.activate_workspace(workspace_idx);\n        }\n    }\n\n    pub fn add_tile_to_column(\n        &mut self,\n        workspace_idx: usize,\n        column_idx: usize,\n        tile_idx: Option<usize>,\n        tile: Tile<W>,\n        activate: bool,\n        // FIXME: Refactor ActivateWindow enum to make this better.\n        allow_to_activate_workspace: bool,\n    ) {\n        let workspace = &mut self.workspaces[workspace_idx];\n\n        workspace.add_tile_to_column(column_idx, tile_idx, tile, activate);\n\n        // After adding a new window, workspace becomes this output's own.\n        if workspace.name().is_none() {\n            workspace.original_output = OutputId::new(&self.output);\n        }\n\n        // Since we're adding window to an existing column, the workspace isn't empty, and\n        // therefore cannot be the last one, so we never need to insert a new empty workspace.\n\n        if allow_to_activate_workspace && activate {\n            self.activate_workspace(workspace_idx);\n        }\n    }\n\n    pub fn clean_up_workspaces(&mut self) {\n        assert!(self.workspace_switch.is_none());\n\n        let range_start = if self.options.layout.empty_workspace_above_first {\n            1\n        } else {\n            0\n        };\n        for idx in (range_start..self.workspaces.len() - 1).rev() {\n            if self.active_workspace_idx == idx {\n                continue;\n            }\n\n            if !self.workspaces[idx].has_windows_or_name() {\n                self.workspaces.remove(idx);\n                if self.active_workspace_idx > idx {\n                    self.active_workspace_idx -= 1;\n                }\n            }\n        }\n\n        // Special case handling when empty_workspace_above_first is set and all workspaces\n        // are empty.\n        if self.options.layout.empty_workspace_above_first && self.workspaces.len() == 2 {\n            assert!(!self.workspaces[0].has_windows_or_name());\n            assert!(!self.workspaces[1].has_windows_or_name());\n            self.workspaces.remove(1);\n            self.active_workspace_idx = 0;\n        }\n    }\n\n    pub fn unname_workspace(&mut self, id: WorkspaceId) -> bool {\n        let Some(ws) = self.workspaces.iter_mut().find(|ws| ws.id() == id) else {\n            return false;\n        };\n\n        ws.unname();\n\n        if self.workspace_switch.is_none() {\n            self.clean_up_workspaces();\n        }\n\n        true\n    }\n\n    pub fn remove_workspace_by_idx(&mut self, mut idx: usize) -> Workspace<W> {\n        if idx == self.workspaces.len() - 1 {\n            self.add_workspace_bottom();\n        }\n        if self.options.layout.empty_workspace_above_first && idx == 0 {\n            self.add_workspace_top();\n            idx += 1;\n        }\n\n        let mut ws = self.workspaces.remove(idx);\n        ws.set_output(None);\n\n        // For monitor current workspace removal, we focus previous rather than next (<= rather\n        // than <). This is different from columns and tiles, but it lets move-workspace-to-monitor\n        // back and forth to preserve position.\n        if idx <= self.active_workspace_idx && self.active_workspace_idx > 0 {\n            self.active_workspace_idx -= 1;\n        }\n\n        self.workspace_switch = None;\n        self.clean_up_workspaces();\n\n        ws\n    }\n\n    pub fn insert_workspace(&mut self, mut ws: Workspace<W>, mut idx: usize, activate: bool) {\n        ws.set_output(Some(self.output.clone()));\n        ws.update_config(self.options.clone());\n\n        // Don't insert past the last empty workspace.\n        if idx == self.workspaces.len() {\n            idx -= 1;\n        }\n        if idx == 0 && self.options.layout.empty_workspace_above_first {\n            // Insert a new empty workspace on top to prepare for insertion of new workspace.\n            self.add_workspace_top();\n            idx += 1;\n        }\n\n        self.workspaces.insert(idx, ws);\n\n        if idx <= self.active_workspace_idx {\n            self.active_workspace_idx += 1;\n        }\n\n        if activate {\n            self.workspace_switch = None;\n            self.activate_workspace(idx);\n        }\n\n        self.workspace_switch = None;\n        self.clean_up_workspaces();\n    }\n\n    pub fn append_workspaces(&mut self, mut workspaces: Vec<Workspace<W>>) {\n        if workspaces.is_empty() {\n            return;\n        }\n\n        for ws in &mut workspaces {\n            ws.set_output(Some(self.output.clone()));\n            ws.update_config(self.options.clone());\n        }\n\n        let empty_was_focused = self.active_workspace_idx == self.workspaces.len() - 1;\n\n        // Push the workspaces from the removed monitor in the end, right before the\n        // last, empty, workspace.\n        let empty = self.workspaces.remove(self.workspaces.len() - 1);\n        self.workspaces.extend(workspaces);\n        self.workspaces.push(empty);\n\n        // If empty_workspace_above_first is set and the first workspace is now no longer empty,\n        // add a new empty workspace on top.\n        if self.options.layout.empty_workspace_above_first\n            && self.workspaces[0].has_windows_or_name()\n        {\n            self.add_workspace_top();\n        }\n\n        // If the empty workspace was focused on the primary monitor, keep it focused.\n        if empty_was_focused {\n            self.active_workspace_idx = self.workspaces.len() - 1;\n        }\n\n        // FIXME: if we're adding workspaces to currently invisible positions\n        // (outside the workspace switch), we don't need to cancel it.\n        self.workspace_switch = None;\n        self.clean_up_workspaces();\n    }\n\n    pub fn move_down_or_to_workspace_down(&mut self) {\n        if !self.active_workspace().move_down() {\n            self.move_to_workspace_down(true);\n        }\n    }\n\n    pub fn move_up_or_to_workspace_up(&mut self) {\n        if !self.active_workspace().move_up() {\n            self.move_to_workspace_up(true);\n        }\n    }\n\n    pub fn focus_window_or_workspace_down(&mut self) {\n        if !self.active_workspace().focus_down() {\n            self.switch_workspace_down();\n        }\n    }\n\n    pub fn focus_window_or_workspace_up(&mut self) {\n        if !self.active_workspace().focus_up() {\n            self.switch_workspace_up();\n        }\n    }\n\n    pub fn move_to_workspace_up(&mut self, focus: bool) {\n        let source_workspace_idx = self.active_workspace_idx;\n\n        let new_idx = source_workspace_idx.saturating_sub(1);\n        if new_idx == source_workspace_idx {\n            return;\n        }\n        let new_id = self.workspaces[new_idx].id();\n\n        let workspace = &mut self.workspaces[source_workspace_idx];\n        let Some(removed) = workspace.remove_active_tile(Transaction::new()) else {\n            return;\n        };\n\n        let activate = if focus {\n            ActivateWindow::Yes\n        } else {\n            ActivateWindow::Smart\n        };\n\n        self.add_tile(\n            removed.tile,\n            MonitorAddWindowTarget::Workspace {\n                id: new_id,\n                column_idx: None,\n            },\n            activate,\n            true,\n            removed.width,\n            removed.is_full_width,\n            removed.is_floating,\n        );\n    }\n\n    pub fn move_to_workspace_down(&mut self, focus: bool) {\n        let source_workspace_idx = self.active_workspace_idx;\n\n        let new_idx = min(source_workspace_idx + 1, self.workspaces.len() - 1);\n        if new_idx == source_workspace_idx {\n            return;\n        }\n        let new_id = self.workspaces[new_idx].id();\n\n        let workspace = &mut self.workspaces[source_workspace_idx];\n        let Some(removed) = workspace.remove_active_tile(Transaction::new()) else {\n            return;\n        };\n\n        let activate = if focus {\n            ActivateWindow::Yes\n        } else {\n            ActivateWindow::Smart\n        };\n\n        self.add_tile(\n            removed.tile,\n            MonitorAddWindowTarget::Workspace {\n                id: new_id,\n                column_idx: None,\n            },\n            activate,\n            true,\n            removed.width,\n            removed.is_full_width,\n            removed.is_floating,\n        );\n    }\n\n    pub fn move_to_workspace(\n        &mut self,\n        window: Option<&W::Id>,\n        idx: usize,\n        activate: ActivateWindow,\n    ) {\n        let source_workspace_idx = if let Some(window) = window {\n            self.workspaces\n                .iter()\n                .position(|ws| ws.has_window(window))\n                .unwrap()\n        } else {\n            self.active_workspace_idx\n        };\n\n        let new_idx = min(idx, self.workspaces.len() - 1);\n        if new_idx == source_workspace_idx {\n            return;\n        }\n        let new_id = self.workspaces[new_idx].id();\n\n        let activate = activate.map_smart(|| {\n            window.is_none_or(|win| self.active_window().map(|win| win.id()) == Some(win))\n        });\n\n        let workspace = &mut self.workspaces[source_workspace_idx];\n        let transaction = Transaction::new();\n        let removed = if let Some(window) = window {\n            workspace.remove_tile(window, transaction)\n        } else if let Some(removed) = workspace.remove_active_tile(transaction) {\n            removed\n        } else {\n            return;\n        };\n\n        self.add_tile(\n            removed.tile,\n            MonitorAddWindowTarget::Workspace {\n                id: new_id,\n                column_idx: None,\n            },\n            if activate {\n                ActivateWindow::Yes\n            } else {\n                ActivateWindow::No\n            },\n            true,\n            removed.width,\n            removed.is_full_width,\n            removed.is_floating,\n        );\n\n        if self.workspace_switch.is_none() {\n            self.clean_up_workspaces();\n        }\n    }\n\n    pub fn move_column_to_workspace_up(&mut self, activate: bool) {\n        let source_workspace_idx = self.active_workspace_idx;\n\n        let new_idx = source_workspace_idx.saturating_sub(1);\n        if new_idx == source_workspace_idx {\n            return;\n        }\n\n        let workspace = &mut self.workspaces[source_workspace_idx];\n        if workspace.floating_is_active() {\n            self.move_to_workspace_up(activate);\n            return;\n        }\n\n        let Some(column) = workspace.remove_active_column() else {\n            return;\n        };\n\n        self.add_column(new_idx, column, activate);\n    }\n\n    pub fn move_column_to_workspace_down(&mut self, activate: bool) {\n        let source_workspace_idx = self.active_workspace_idx;\n\n        let new_idx = min(source_workspace_idx + 1, self.workspaces.len() - 1);\n        if new_idx == source_workspace_idx {\n            return;\n        }\n\n        let workspace = &mut self.workspaces[source_workspace_idx];\n        if workspace.floating_is_active() {\n            self.move_to_workspace_down(activate);\n            return;\n        }\n\n        let Some(column) = workspace.remove_active_column() else {\n            return;\n        };\n\n        self.add_column(new_idx, column, activate);\n    }\n\n    pub fn move_column_to_workspace(&mut self, idx: usize, activate: bool) {\n        let source_workspace_idx = self.active_workspace_idx;\n\n        let new_idx = min(idx, self.workspaces.len() - 1);\n        if new_idx == source_workspace_idx {\n            return;\n        }\n\n        let workspace = &mut self.workspaces[source_workspace_idx];\n        if workspace.floating_is_active() {\n            let activate = if activate {\n                ActivateWindow::Smart\n            } else {\n                ActivateWindow::No\n            };\n            self.move_to_workspace(None, idx, activate);\n            return;\n        }\n\n        let Some(column) = workspace.remove_active_column() else {\n            return;\n        };\n\n        self.add_column(new_idx, column, activate);\n    }\n\n    pub fn switch_workspace_up(&mut self) {\n        let new_idx = match &self.workspace_switch {\n            // During a DnD scroll, select the prev apparent workspace.\n            Some(WorkspaceSwitch::Gesture(gesture)) if gesture.dnd_last_event_time.is_some() => {\n                let current = gesture.current_idx;\n                let new = current.ceil() - 1.;\n                new.clamp(0., (self.workspaces.len() - 1) as f64) as usize\n            }\n            _ => self.active_workspace_idx.saturating_sub(1),\n        };\n\n        self.activate_workspace(new_idx);\n    }\n\n    pub fn switch_workspace_down(&mut self) {\n        let new_idx = match &self.workspace_switch {\n            // During a DnD scroll, select the next apparent workspace.\n            Some(WorkspaceSwitch::Gesture(gesture)) if gesture.dnd_last_event_time.is_some() => {\n                let current = gesture.current_idx;\n                let new = current.floor() + 1.;\n                new.clamp(0., (self.workspaces.len() - 1) as f64) as usize\n            }\n            _ => min(self.active_workspace_idx + 1, self.workspaces.len() - 1),\n        };\n\n        self.activate_workspace(new_idx);\n    }\n\n    fn previous_workspace_idx(&self) -> Option<usize> {\n        let id = self.previous_workspace_id?;\n        self.workspaces.iter().position(|w| w.id() == id)\n    }\n\n    pub fn switch_workspace(&mut self, idx: usize) {\n        self.activate_workspace(min(idx, self.workspaces.len() - 1));\n    }\n\n    pub fn switch_workspace_auto_back_and_forth(&mut self, idx: usize) {\n        let idx = min(idx, self.workspaces.len() - 1);\n\n        if idx == self.active_workspace_idx {\n            if let Some(prev_idx) = self.previous_workspace_idx() {\n                self.switch_workspace(prev_idx);\n            }\n        } else {\n            self.switch_workspace(idx);\n        }\n    }\n\n    pub fn switch_workspace_previous(&mut self) {\n        if let Some(idx) = self.previous_workspace_idx() {\n            self.switch_workspace(idx);\n        }\n    }\n\n    pub fn active_window(&self) -> Option<&W> {\n        self.active_workspace_ref().active_window()\n    }\n\n    pub fn advance_animations(&mut self) {\n        match &mut self.workspace_switch {\n            Some(WorkspaceSwitch::Animation(anim)) => {\n                if anim.is_done() {\n                    self.workspace_switch = None;\n                    self.clean_up_workspaces();\n                }\n            }\n            Some(WorkspaceSwitch::Gesture(gesture)) => {\n                // Make sure the last event time doesn't go too much out of date (for\n                // monitors not under cursor), causing sudden jumps.\n                //\n                // This happens after any dnd_scroll_gesture_scroll() calls (in\n                // Layout::advance_animations()), so it doesn't mess up the time delta there.\n                if let Some(last_time) = &mut gesture.dnd_last_event_time {\n                    let now = self.clock.now_unadjusted();\n                    if *last_time != now {\n                        *last_time = now;\n\n                        // If last_time was already == now, then dnd_scroll_gesture_scroll() must've\n                        // updated the gesture already. Therefore, when this code runs, the pointer\n                        // must be outside the DnD scrolling zone.\n                        gesture.dnd_nonzero_start_time = None;\n                    }\n                }\n\n                if let Some(anim) = &mut gesture.animation {\n                    if anim.is_done() {\n                        gesture.animation = None;\n                    }\n                }\n            }\n            None => (),\n        }\n\n        for ws in &mut self.workspaces {\n            ws.advance_animations();\n        }\n    }\n\n    pub(super) fn are_animations_ongoing(&self) -> bool {\n        self.workspace_switch\n            .as_ref()\n            .is_some_and(|s| s.is_animation_ongoing())\n            || self.workspaces.iter().any(|ws| ws.are_animations_ongoing())\n    }\n\n    pub fn are_transitions_ongoing(&self) -> bool {\n        self.workspace_switch.is_some()\n            || self\n                .workspaces\n                .iter()\n                .any(|ws| ws.are_transitions_ongoing())\n    }\n\n    pub fn update_render_elements(&mut self, is_active: bool) {\n        let mut insert_hint_ws_geo = None;\n        let insert_hint_ws_id = self\n            .insert_hint\n            .as_ref()\n            .and_then(|hint| hint.workspace.existing_id());\n\n        for (ws, geo) in self.workspaces_with_render_geo_mut(true) {\n            ws.update_render_elements(is_active);\n\n            if Some(ws.id()) == insert_hint_ws_id {\n                insert_hint_ws_geo = Some(geo);\n            }\n        }\n\n        self.insert_hint_render_loc = None;\n        if let Some(hint) = &self.insert_hint {\n            match hint.workspace {\n                InsertWorkspace::Existing(ws_id) => {\n                    if let Some(ws) = self.workspaces.iter().find(|ws| ws.id() == ws_id) {\n                        if let Some(mut area) = ws.insert_hint_area(hint.position) {\n                            let scale = ws.scale().fractional_scale();\n                            let view_size = ws.view_size();\n\n                            // Make sure the hint is at least partially visible.\n                            if matches!(hint.position, InsertPosition::NewColumn(_)) {\n                                let zoom = self.overview_zoom();\n                                let geo = insert_hint_ws_geo.unwrap();\n                                let geo = geo.downscale(zoom);\n\n                                area.loc.x = area.loc.x.max(-geo.loc.x - area.size.w / 2.);\n                                area.loc.x =\n                                    area.loc.x.min(geo.loc.x + geo.size.w - area.size.w / 2.);\n                            }\n\n                            // Round to physical pixels.\n                            area = area.to_physical_precise_round(scale).to_logical(scale);\n\n                            let view_rect = Rectangle::new(area.loc.upscale(-1.), view_size);\n                            self.insert_hint_element.update_render_elements(\n                                area.size,\n                                view_rect,\n                                hint.corner_radius,\n                                scale,\n                            );\n                            self.insert_hint_render_loc = Some(InsertHintRenderLoc {\n                                workspace: hint.workspace,\n                                location: area.loc,\n                            });\n                        }\n                    } else {\n                        error!(\"insert hint workspace missing from monitor\");\n                    }\n                }\n                InsertWorkspace::NewAt(ws_idx) => {\n                    let scale = self.scale.fractional_scale();\n                    let zoom = self.overview_zoom();\n                    let gap = self.workspace_gap(zoom);\n\n                    let hint_gap = round_logical_in_physical(scale, gap * 0.1);\n                    let hint_height = gap - hint_gap * 2.;\n\n                    let next_ws_geo = self.workspaces_render_geo().nth(ws_idx).unwrap();\n                    let hint_width = round_logical_in_physical(scale, next_ws_geo.size.w * 0.75);\n                    let hint_x =\n                        round_logical_in_physical(scale, (next_ws_geo.size.w - hint_width) / 2.);\n\n                    let hint_loc_diff = Point::from((-hint_x, hint_height + hint_gap));\n                    let hint_loc = next_ws_geo.loc - hint_loc_diff;\n                    let hint_size = Size::from((hint_width, hint_height));\n\n                    // Sometimes the hint ends up 1 px wider than necessary and/or 1 px\n                    // narrower than necessary. The values here seem correct. Might have to do with\n                    // how zooming out currently doesn't round to output scale properly.\n\n                    // Compute view rect as if we're above the next workspace (rather than below\n                    // the previous one).\n                    let view_rect = Rectangle::new(hint_loc_diff, next_ws_geo.size);\n\n                    self.insert_hint_element.update_render_elements(\n                        hint_size,\n                        view_rect,\n                        CornerRadius::default(),\n                        scale,\n                    );\n                    self.insert_hint_render_loc = Some(InsertHintRenderLoc {\n                        workspace: hint.workspace,\n                        location: hint_loc,\n                    });\n                }\n            }\n        }\n    }\n\n    pub fn update_config(&mut self, base_options: Rc<Options>) {\n        let options =\n            Rc::new(Options::clone(&base_options).with_merged_layout(self.layout_config.as_ref()));\n\n        if self.options.layout.empty_workspace_above_first\n            != options.layout.empty_workspace_above_first\n            && self.workspaces.len() > 1\n        {\n            if options.layout.empty_workspace_above_first {\n                self.add_workspace_top();\n            } else if self.workspace_switch.is_none() && self.active_workspace_idx != 0 {\n                self.workspaces.remove(0);\n                self.active_workspace_idx = self.active_workspace_idx.saturating_sub(1);\n            }\n        }\n\n        for ws in &mut self.workspaces {\n            ws.update_config(options.clone());\n        }\n\n        self.insert_hint_element\n            .update_config(options.layout.insert_hint);\n\n        self.base_options = base_options;\n        self.options = options;\n    }\n\n    pub fn update_layout_config(&mut self, layout_config: Option<niri_config::LayoutPart>) -> bool {\n        if self.layout_config == layout_config {\n            return false;\n        }\n\n        self.layout_config = layout_config;\n        self.update_config(self.base_options.clone());\n\n        true\n    }\n\n    pub fn update_shaders(&mut self) {\n        for ws in &mut self.workspaces {\n            ws.update_shaders();\n        }\n\n        self.insert_hint_element.update_shaders();\n    }\n\n    pub fn update_output_size(&mut self) {\n        self.scale = self.output.current_scale();\n        self.view_size = output_size(&self.output);\n        self.working_area = compute_working_area(&self.output);\n\n        for ws in &mut self.workspaces {\n            ws.update_output_size();\n        }\n    }\n\n    pub fn move_workspace_down(&mut self) {\n        let mut new_idx = min(self.active_workspace_idx + 1, self.workspaces.len() - 1);\n        if new_idx == self.active_workspace_idx {\n            return;\n        }\n\n        self.workspaces.swap(self.active_workspace_idx, new_idx);\n\n        if new_idx == self.workspaces.len() - 1 {\n            // Insert a new empty workspace.\n            self.add_workspace_bottom();\n        }\n\n        if self.options.layout.empty_workspace_above_first && self.active_workspace_idx == 0 {\n            self.add_workspace_top();\n            new_idx += 1;\n        }\n\n        let previous_workspace_id = self.previous_workspace_id;\n        self.activate_workspace(new_idx);\n        self.workspace_switch = None;\n        self.previous_workspace_id = previous_workspace_id;\n\n        self.clean_up_workspaces();\n    }\n\n    pub fn move_workspace_up(&mut self) {\n        let mut new_idx = self.active_workspace_idx.saturating_sub(1);\n        if new_idx == self.active_workspace_idx {\n            return;\n        }\n\n        self.workspaces.swap(self.active_workspace_idx, new_idx);\n\n        if self.active_workspace_idx == self.workspaces.len() - 1 {\n            // Insert a new empty workspace.\n            self.add_workspace_bottom();\n        }\n\n        if self.options.layout.empty_workspace_above_first && new_idx == 0 {\n            self.add_workspace_top();\n            new_idx += 1;\n        }\n\n        let previous_workspace_id = self.previous_workspace_id;\n        self.activate_workspace(new_idx);\n        self.workspace_switch = None;\n        self.previous_workspace_id = previous_workspace_id;\n\n        self.clean_up_workspaces();\n    }\n\n    pub fn move_workspace_to_idx(&mut self, old_idx: usize, new_idx: usize) {\n        if self.workspaces.len() <= old_idx {\n            return;\n        }\n\n        let mut new_idx = new_idx.clamp(0, self.workspaces.len() - 1);\n        if old_idx == new_idx {\n            return;\n        }\n\n        let ws = self.workspaces.remove(old_idx);\n        self.workspaces.insert(new_idx, ws);\n\n        if new_idx > old_idx {\n            if new_idx == self.workspaces.len() - 1 {\n                // Insert a new empty workspace.\n                self.add_workspace_bottom();\n            }\n\n            if self.options.layout.empty_workspace_above_first && old_idx == 0 {\n                self.add_workspace_top();\n                new_idx += 1;\n            }\n        } else {\n            if old_idx == self.workspaces.len() - 1 {\n                // Insert a new empty workspace.\n                self.add_workspace_bottom();\n            }\n\n            if self.options.layout.empty_workspace_above_first && new_idx == 0 {\n                self.add_workspace_top();\n                new_idx += 1;\n            }\n        }\n\n        // Only refocus the workspace if it was already focused\n        if self.active_workspace_idx == old_idx {\n            self.active_workspace_idx = new_idx;\n        // If the workspace order was switched so that the current workspace moved down the\n        // workspace stack, focus correctly\n        } else if new_idx <= self.active_workspace_idx && old_idx > self.active_workspace_idx {\n            self.active_workspace_idx += 1;\n        } else if new_idx >= self.active_workspace_idx && old_idx < self.active_workspace_idx {\n            self.active_workspace_idx = self.active_workspace_idx.saturating_sub(1);\n        }\n\n        self.workspace_switch = None;\n\n        self.clean_up_workspaces();\n    }\n\n    /// Returns the geometry of the active tile relative to and clamped to the output.\n    ///\n    /// During animations, assumes the final view position.\n    pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {\n        if self.overview_open {\n            return None;\n        }\n\n        self.active_workspace_ref().active_tile_visual_rectangle()\n    }\n\n    fn workspace_size(&self, zoom: f64) -> Size<f64, Logical> {\n        let ws_size = self.view_size.upscale(zoom);\n        let scale = self.scale.fractional_scale();\n        ws_size.to_physical_precise_ceil(scale).to_logical(scale)\n    }\n\n    fn workspace_gap(&self, zoom: f64) -> f64 {\n        let scale = self.scale.fractional_scale();\n        let gap = self.view_size.h * 0.1 * zoom;\n        round_logical_in_physical_max1(scale, gap)\n    }\n\n    fn workspace_size_with_gap(&self, zoom: f64) -> Size<f64, Logical> {\n        let gap = self.workspace_gap(zoom);\n        self.workspace_size(zoom) + Size::from((0., gap))\n    }\n\n    pub fn overview_zoom(&self) -> f64 {\n        let progress = self.overview_progress.as_ref().map(|p| p.value());\n        compute_overview_zoom(&self.options, progress)\n    }\n\n    pub(super) fn set_overview_progress(&mut self, progress: Option<&super::OverviewProgress>) {\n        let prev_render_idx = self.workspace_render_idx();\n        self.overview_progress = progress.map(OverviewProgress::from);\n        let new_render_idx = self.workspace_render_idx();\n\n        // If the view jumped (can happen when going from corrected to uncorrected render_idx, for\n        // example when toggling the overview in the middle of an overview animation), then restart\n        // the workspace switch to avoid jumps.\n        if prev_render_idx != new_render_idx {\n            if let Some(WorkspaceSwitch::Animation(anim)) = &mut self.workspace_switch {\n                // FIXME: maintain velocity.\n                *anim = anim.restarted(prev_render_idx, anim.to(), 0.);\n            }\n        }\n    }\n\n    #[cfg(test)]\n    pub(super) fn overview_progress_value(&self) -> Option<f64> {\n        self.overview_progress.as_ref().map(|p| p.value())\n    }\n\n    pub fn workspace_render_idx(&self) -> f64 {\n        // If workspace switch and overview progress are matching animations, then compute a\n        // correction term to make the movement appear monotonic.\n        if let (\n            Some(WorkspaceSwitch::Animation(switch_anim)),\n            Some(OverviewProgress::Animation(progress_anim)),\n        ) = (&self.workspace_switch, &self.overview_progress)\n        {\n            if switch_anim.start_time() == progress_anim.start_time()\n                && (switch_anim.duration().as_secs_f64() - progress_anim.duration().as_secs_f64())\n                    .abs()\n                    <= 0.001\n            {\n                #[rustfmt::skip]\n                // How this was derived:\n                //\n                // - Assume we're animating a zoom + switch. Consider switch \"from\" and \"to\".\n                //   These are render_idx values, so first workspace to second would have switch\n                //   from = 0. and to = 1. regardless of the zoom level.\n                //\n                // - At the start, the point at \"from\" is at Y = 0. We're moving the point at \"to\"\n                //   to Y = 0. We want this to be a monotonic motion in apparent coordinates (after\n                //   zoom).\n                //\n                // - Height at the start:\n                //   from_height = (size.h + gap) * from_zoom.\n                //\n                // - Current height:\n                //   current_height = (size.h + gap) * zoom.\n                //\n                // - We're moving the \"to\" point to Y = 0:\n                //   to_y = 0.\n                //\n                // - The initial position of the point we're moving:\n                //   from_y = (to - from) * from_height.\n                //\n                // - We want this point to travel monotonically in apparent coordinates:\n                //   current_y = from_y + (to_y - from_y) * progress,\n                //   where progress is from 0 to 1, equals to the animation progress (switch and\n                //   zoom are the same since they are synchronized).\n                //\n                // - Derive the Y of the first workspace from this:\n                //   first_y = current_y - to * current_height.\n                //\n                // Now, let's substitute and rearrange the terms.\n                //\n                // - current_y = from_y + (0 - (to - from) * from_height) * progress\n                // - progress = (switch_anim.value() - from) / (to - from)\n                // - current_y = from_y - (to - from) * from_height * (switch_anim.value() - from) / (to - from)\n                // - current_y = from_y - from_height * (switch_anim.value() - from)\n                // - first_y = from_y - from_height * (switch_anim.value() - from) - to * current_height\n                // - first_y = (to - from) * from_height - from_height * (switch_anim.value() - from) - to * current_height\n                // - first_y = to * from_height - switch_anim.value() * from_height - to * current_height\n                // - first_y = -switch_anim.value() * from_height + to * (from_height - current_height)\n                let from = progress_anim.from();\n                let from_zoom = compute_overview_zoom(&self.options, Some(from));\n                let from_ws_height_with_gap = self.workspace_size_with_gap(from_zoom).h;\n\n                let zoom = self.overview_zoom();\n                let ws_height_with_gap = self.workspace_size_with_gap(zoom).h;\n\n                let first_ws_y = -switch_anim.value() * from_ws_height_with_gap\n                    + switch_anim.to() * (from_ws_height_with_gap - ws_height_with_gap);\n\n                return -first_ws_y / ws_height_with_gap;\n            }\n        };\n\n        if let Some(switch) = &self.workspace_switch {\n            switch.current_idx()\n        } else {\n            self.active_workspace_idx as f64\n        }\n    }\n\n    pub fn workspaces_render_geo(&self) -> impl Iterator<Item = Rectangle<f64, Logical>> {\n        let scale = self.scale.fractional_scale();\n        let zoom = self.overview_zoom();\n\n        let ws_size = self.workspace_size(zoom);\n        let gap = self.workspace_gap(zoom);\n        let ws_height_with_gap = ws_size.h + gap;\n\n        let static_offset = (self.view_size.to_point() - ws_size.to_point()).downscale(2.);\n        let static_offset = static_offset\n            .to_physical_precise_round(scale)\n            .to_logical(scale);\n\n        let first_ws_y = -self.workspace_render_idx() * ws_height_with_gap;\n        let first_ws_y = round_logical_in_physical(scale, first_ws_y);\n\n        // Return position for one-past-last workspace too.\n        (0..=self.workspaces.len()).map(move |idx| {\n            let y = first_ws_y + idx as f64 * ws_height_with_gap;\n            let loc = Point::from((0., y)) + static_offset;\n\n            // Even though all components that go into loc are rounded to physical pixels, the\n            // floating point addition may lose precision. This can result for example in the\n            // current workspace having y = 0.0000000000002 and thus missing pointer hits at the\n            // monitor edge with y = 0. So, post-round the location too.\n            let loc = loc.to_physical_precise_round(scale).to_logical(scale);\n\n            Rectangle::new(loc, ws_size)\n        })\n    }\n\n    pub fn workspaces_with_render_geo(\n        &self,\n    ) -> impl Iterator<Item = (&Workspace<W>, Rectangle<f64, Logical>)> {\n        let output_geo = Rectangle::from_size(self.view_size);\n\n        let geo = self.workspaces_render_geo();\n        zip(self.workspaces.iter(), geo)\n            // Cull out workspaces outside the output.\n            .filter(move |(_ws, geo)| geo.intersection(output_geo).is_some())\n    }\n\n    pub fn workspaces_with_render_geo_idx(\n        &self,\n    ) -> impl Iterator<Item = ((usize, &Workspace<W>), Rectangle<f64, Logical>)> {\n        let output_geo = Rectangle::from_size(self.view_size);\n\n        let geo = self.workspaces_render_geo();\n        zip(self.workspaces.iter().enumerate(), geo)\n            // Cull out workspaces outside the output.\n            .filter(move |(_ws, geo)| geo.intersection(output_geo).is_some())\n    }\n\n    pub fn workspaces_with_render_geo_mut(\n        &mut self,\n        cull: bool,\n    ) -> impl Iterator<Item = (&mut Workspace<W>, Rectangle<f64, Logical>)> {\n        let output_geo = Rectangle::from_size(self.view_size);\n\n        let geo = self.workspaces_render_geo();\n        zip(self.workspaces.iter_mut(), geo)\n            // Cull out workspaces outside the output.\n            .filter(move |(_ws, geo)| !cull || geo.intersection(output_geo).is_some())\n    }\n\n    pub fn workspace_under(\n        &self,\n        pos_within_output: Point<f64, Logical>,\n    ) -> Option<(&Workspace<W>, Rectangle<f64, Logical>)> {\n        let (ws, geo) = self.workspaces_with_render_geo().find_map(|(ws, geo)| {\n            // Extend width to entire output.\n            let loc = Point::from((0., geo.loc.y));\n            let size = Size::from((self.view_size.w, geo.size.h));\n            let bounds = Rectangle::new(loc, size);\n\n            bounds.contains(pos_within_output).then_some((ws, geo))\n        })?;\n        Some((ws, geo))\n    }\n\n    pub fn workspace_under_narrow(\n        &self,\n        pos_within_output: Point<f64, Logical>,\n    ) -> Option<&Workspace<W>> {\n        self.workspaces_with_render_geo()\n            .find_map(|(ws, geo)| geo.contains(pos_within_output).then_some(ws))\n    }\n\n    pub fn window_under(&self, pos_within_output: Point<f64, Logical>) -> Option<(&W, HitType)> {\n        let (ws, geo) = self.workspace_under(pos_within_output)?;\n\n        if self.overview_progress.is_some() {\n            let zoom = self.overview_zoom();\n            let pos_within_workspace = (pos_within_output - geo.loc).downscale(zoom);\n            let (win, hit) = ws.window_under(pos_within_workspace)?;\n            // During the overview animation, we cannot do input hits because we cannot really\n            // represent scaled windows properly.\n            Some((win, hit.to_activate()))\n        } else {\n            let (win, hit) = ws.window_under(pos_within_output - geo.loc)?;\n            Some((win, hit.offset_win_pos(geo.loc)))\n        }\n    }\n\n    pub fn resize_edges_under(&self, pos_within_output: Point<f64, Logical>) -> Option<ResizeEdge> {\n        if self.overview_progress.is_some() {\n            return None;\n        }\n\n        let (ws, geo) = self.workspace_under(pos_within_output)?;\n        ws.resize_edges_under(pos_within_output - geo.loc)\n    }\n\n    pub(super) fn insert_position(\n        &self,\n        pos_within_output: Point<f64, Logical>,\n    ) -> (InsertWorkspace, Rectangle<f64, Logical>) {\n        let mut iter = self.workspaces_with_render_geo_idx();\n\n        let dummy = Rectangle::default();\n\n        // Monitors always have at least one workspace.\n        let ((idx, ws), geo) = iter.next().unwrap();\n\n        // Check if above first.\n        if pos_within_output.y < geo.loc.y {\n            return (InsertWorkspace::NewAt(idx), dummy);\n        }\n\n        let contains = move |geo: Rectangle<f64, Logical>| {\n            geo.loc.y <= pos_within_output.y && pos_within_output.y < geo.loc.y + geo.size.h\n        };\n\n        // Check first.\n        if contains(geo) {\n            return (InsertWorkspace::Existing(ws.id()), geo);\n        }\n\n        let mut last_geo = geo;\n        let mut last_idx = idx;\n        for ((idx, ws), geo) in iter {\n            // Check gap above.\n            let gap_loc = Point::from((last_geo.loc.x, last_geo.loc.y + last_geo.size.h));\n            let gap_size = Size::from((geo.size.w, geo.loc.y - gap_loc.y));\n            let gap_geo = Rectangle::new(gap_loc, gap_size);\n            if contains(gap_geo) {\n                return (InsertWorkspace::NewAt(idx), dummy);\n            }\n\n            // Check workspace itself.\n            if contains(geo) {\n                return (InsertWorkspace::Existing(ws.id()), geo);\n            }\n\n            last_geo = geo;\n            last_idx = idx;\n        }\n\n        // Anything below.\n        (InsertWorkspace::NewAt(last_idx + 1), dummy)\n    }\n\n    pub fn render_above_top_layer(&self) -> bool {\n        // Render above the top layer only if the view is stationary.\n        if self.workspace_switch.is_some() || self.overview_progress.is_some() {\n            return false;\n        }\n\n        let ws = &self.workspaces[self.active_workspace_idx];\n        ws.render_above_top_layer()\n    }\n\n    pub fn render_insert_hint_between_workspaces<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        push: &mut dyn FnMut(MonitorRenderElement<R>),\n    ) {\n        if self.options.layout.insert_hint.off {\n            return;\n        }\n        let Some(render_loc) = self.insert_hint_render_loc else {\n            return;\n        };\n        let InsertWorkspace::NewAt(_) = render_loc.workspace else {\n            return;\n        };\n\n        self.insert_hint_element\n            .render(renderer, render_loc.location, &mut |elem| {\n                let elem = MonitorInnerRenderElement::UncroppedInsertHint(elem);\n                let elem = RescaleRenderElement::from_element(elem, Point::default(), 1.);\n                let elem =\n                    RelocateRenderElement::from_element(elem, Point::default(), Relocate::Relative);\n                push(elem);\n            });\n    }\n\n    pub fn render_workspaces<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        target: RenderTarget,\n        focus_ring: bool,\n        push: &mut dyn FnMut(MonitorRenderElement<R>),\n    ) {\n        let _span = tracy_client::span!(\"Monitor::render_workspaces\");\n\n        let scale = self.scale.fractional_scale();\n        // Ceil the height in physical pixels.\n        let height = (self.view_size.h * scale).ceil() as i32;\n\n        // Crop the elements to prevent them overflowing, currently visible during a workspace\n        // switch.\n        //\n        // HACK: crop to infinite bounds at least horizontally where we\n        // know there's no workspace joining or monitor bounds, otherwise\n        // it will cut pixel shaders and mess up the coordinate space.\n        // There's also a damage tracking bug which causes glitched\n        // rendering for maximized GTK windows.\n        //\n        // FIXME: use proper bounds after fixing the Crop element.\n        let crop_bounds = if self.workspace_switch.is_some() || self.overview_progress.is_some() {\n            Rectangle::new(\n                Point::from((-i32::MAX / 2, 0)),\n                Size::from((i32::MAX, height)),\n            )\n        } else {\n            Rectangle::new(\n                Point::from((-i32::MAX / 2, -i32::MAX / 2)),\n                Size::from((i32::MAX, i32::MAX)),\n            )\n        };\n\n        let zoom = self.overview_zoom();\n\n        let insert_hint_render_loc = self\n            .insert_hint_render_loc\n            .filter(|_| !self.options.layout.insert_hint.off);\n\n        let scale_relocate = move |geo: Rectangle<f64, Logical>, elem| {\n            let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom);\n            RelocateRenderElement::from_element(\n                elem,\n                // The offset we get from workspaces_with_render_geo() is already\n                // rounded to physical pixels, but it's in the logical coordinate\n                // space, so we need to convert it to physical.\n                geo.loc.to_physical_precise_round(scale),\n                Relocate::Relative,\n            )\n        };\n\n        for (ws, geo) in self.workspaces_with_render_geo() {\n            // Macro instead of closure because ws and insert hint have different elem types.\n            macro_rules! push {\n                () => {{\n                    &mut |elem| {\n                        let elem = CropRenderElement::from_element(elem, scale, crop_bounds);\n                        if let Some(elem) = elem {\n                            let elem = MonitorInnerRenderElement::from(elem);\n                            push(scale_relocate(geo, elem));\n                        }\n                    }\n                }};\n            }\n\n            ws.render_floating(renderer, target, focus_ring, push!());\n\n            if let Some(loc) = insert_hint_render_loc {\n                if loc.workspace == InsertWorkspace::Existing(ws.id()) {\n                    self.insert_hint_element\n                        .render(renderer, loc.location, push!());\n                }\n            }\n\n            ws.render_scrolling(renderer, target, focus_ring, push!());\n        }\n    }\n\n    pub fn render_workspace_shadows<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        push: &mut dyn FnMut(MonitorRenderElement<R>),\n    ) {\n        let Some(progress) = self.overview_progress.as_ref().map(|p| p.clamped_value()) else {\n            return;\n        };\n        let alpha = progress.clamp(0., 1.) as f32;\n\n        let _span = tracy_client::span!(\"Monitor::render_workspace_shadows\");\n\n        let scale = self.scale.fractional_scale();\n        let zoom = self.overview_zoom();\n\n        for (ws, geo) in self.workspaces_with_render_geo() {\n            ws.render_shadow(renderer, &mut |elem| {\n                let elem = elem.with_alpha(alpha);\n                let elem = MonitorInnerRenderElement::Shadow(elem);\n                let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom);\n                let elem = RelocateRenderElement::from_element(\n                    elem,\n                    geo.loc.to_physical_precise_round(scale),\n                    Relocate::Relative,\n                );\n                push(elem);\n            });\n        }\n    }\n\n    pub fn workspace_switch_gesture_begin(&mut self, is_touchpad: bool) {\n        let center_idx = self.active_workspace_idx;\n        let current_idx = self.workspace_render_idx();\n\n        let gesture = WorkspaceSwitchGesture {\n            center_idx,\n            start_idx: current_idx,\n            current_idx,\n            animation: None,\n            tracker: SwipeTracker::new(),\n            is_touchpad,\n            is_clamped: !self.overview_open,\n            dnd_last_event_time: None,\n            dnd_nonzero_start_time: None,\n        };\n        self.workspace_switch = Some(WorkspaceSwitch::Gesture(gesture));\n    }\n\n    pub fn dnd_scroll_gesture_begin(&mut self) {\n        if let Some(WorkspaceSwitch::Gesture(WorkspaceSwitchGesture {\n            dnd_last_event_time: Some(_),\n            ..\n        })) = &self.workspace_switch\n        {\n            // Already active.\n            return;\n        }\n\n        if !self.overview_open {\n            // This gesture is only for the overview.\n            return;\n        }\n\n        let center_idx = self.active_workspace_idx;\n        let current_idx = self.workspace_render_idx();\n\n        let gesture = WorkspaceSwitchGesture {\n            center_idx,\n            start_idx: current_idx,\n            current_idx,\n            animation: None,\n            tracker: SwipeTracker::new(),\n            is_touchpad: false,\n            is_clamped: false,\n            dnd_last_event_time: Some(self.clock.now_unadjusted()),\n            dnd_nonzero_start_time: None,\n        };\n        self.workspace_switch = Some(WorkspaceSwitch::Gesture(gesture));\n    }\n\n    pub fn workspace_switch_gesture_update(\n        &mut self,\n        delta_y: f64,\n        timestamp: Duration,\n        is_touchpad: bool,\n    ) -> Option<bool> {\n        let Some(WorkspaceSwitch::Gesture(gesture)) = &self.workspace_switch else {\n            return None;\n        };\n\n        if gesture.is_touchpad != is_touchpad || gesture.dnd_last_event_time.is_some() {\n            return None;\n        }\n\n        let zoom = self.overview_zoom();\n        let total_height = if gesture.is_touchpad {\n            WORKSPACE_GESTURE_MOVEMENT\n        } else {\n            self.workspace_size_with_gap(1.).h\n        };\n\n        let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else {\n            return None;\n        };\n\n        // Reduce the effect of zoom on the touchpad somewhat.\n        let delta_scale = if gesture.is_touchpad {\n            (zoom - 1.) / 2.5 + 1.\n        } else {\n            zoom\n        };\n\n        let delta_y = delta_y / delta_scale;\n        let mut rubber_band = WORKSPACE_GESTURE_RUBBER_BAND;\n        rubber_band.limit /= zoom;\n\n        gesture.tracker.push(delta_y, timestamp);\n\n        let pos = gesture.tracker.pos() / total_height;\n\n        let (min, max) = gesture.min_max(self.workspaces.len());\n        let new_idx = gesture.start_idx + pos;\n        let new_idx = rubber_band.clamp(min, max, new_idx);\n\n        if gesture.current_idx == new_idx {\n            return Some(false);\n        }\n\n        gesture.current_idx = new_idx;\n        Some(true)\n    }\n\n    pub fn dnd_scroll_gesture_scroll(&mut self, pos: Point<f64, Logical>, speed: f64) -> bool {\n        let zoom = self.overview_zoom();\n\n        let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else {\n            return false;\n        };\n\n        let Some(last_time) = gesture.dnd_last_event_time else {\n            // Not a DnD scroll.\n            return false;\n        };\n\n        let config = &self.options.gestures.dnd_edge_workspace_switch;\n        let trigger_height = config.trigger_height;\n\n        // Restrict the scrolling horizontally to the strip of workspaces to avoid unwanted trigger\n        // after using the hot corner or during horizontal scroll.\n        let width = self.view_size.w * zoom;\n        let x = pos.x - (self.view_size.w - width) / 2.;\n\n        // Consider the working area so layer-shell docks and such don't prevent scrolling.\n        let y = pos.y - self.working_area.loc.y;\n        let height = self.working_area.size.h;\n\n        let y = y.clamp(0., height);\n        let trigger_height = trigger_height.clamp(0., height / 2.);\n\n        let delta = if x < 0. || width <= x {\n            // Outside the bounds horizontally.\n            0.\n        } else if y < trigger_height {\n            -(trigger_height - y)\n        } else if height - y < trigger_height {\n            trigger_height - (height - y)\n        } else {\n            0.\n        };\n\n        let delta = if trigger_height < 0.01 {\n            // Sanity check for trigger-height 0 or small window sizes.\n            0.\n        } else {\n            // Normalize to [0, 1].\n            delta / trigger_height\n        };\n        let delta = delta * speed;\n\n        let now = self.clock.now_unadjusted();\n        gesture.dnd_last_event_time = Some(now);\n\n        if delta == 0. {\n            // We're outside the scrolling zone.\n            gesture.dnd_nonzero_start_time = None;\n            return false;\n        }\n\n        let nonzero_start = *gesture.dnd_nonzero_start_time.get_or_insert(now);\n\n        // Delay starting the gesture a bit to avoid unwanted movement when dragging across\n        // monitors.\n        let delay = Duration::from_millis(u64::from(config.delay_ms));\n        if now.saturating_sub(nonzero_start) < delay {\n            return true;\n        }\n\n        let time_delta = now.saturating_sub(last_time).as_secs_f64();\n\n        let delta = delta * time_delta * config.max_speed;\n\n        gesture.tracker.push(delta, now);\n\n        let total_height = WORKSPACE_DND_EDGE_SCROLL_MOVEMENT;\n        let pos = gesture.tracker.pos() / total_height;\n        let unclamped = gesture.start_idx + pos;\n\n        let (min, max) = gesture.min_max(self.workspaces.len());\n        let clamped = unclamped.clamp(min, max);\n\n        // Make sure that DnD scrolling too much outside the min/max does not \"build up\".\n        gesture.start_idx += clamped - unclamped;\n        gesture.current_idx = clamped;\n\n        true\n    }\n\n    pub fn workspace_switch_gesture_end(&mut self, is_touchpad: Option<bool>) -> bool {\n        let Some(WorkspaceSwitch::Gesture(gesture)) = &self.workspace_switch else {\n            return false;\n        };\n\n        if is_touchpad.is_some_and(|x| gesture.is_touchpad != x) {\n            return false;\n        }\n\n        let zoom = self.overview_zoom();\n        let total_height = if gesture.dnd_last_event_time.is_some() {\n            WORKSPACE_DND_EDGE_SCROLL_MOVEMENT\n        } else if gesture.is_touchpad {\n            WORKSPACE_GESTURE_MOVEMENT\n        } else {\n            self.workspace_size_with_gap(1.).h\n        };\n\n        let Some(WorkspaceSwitch::Gesture(gesture)) = &mut self.workspace_switch else {\n            return false;\n        };\n\n        // Take into account any idle time between the last event and now.\n        let now = self.clock.now_unadjusted();\n        gesture.tracker.push(0., now);\n\n        let mut rubber_band = WORKSPACE_GESTURE_RUBBER_BAND;\n        rubber_band.limit /= zoom;\n\n        let mut velocity = gesture.tracker.velocity() / total_height;\n        let current_pos = gesture.tracker.pos() / total_height;\n        let pos = gesture.tracker.projected_end_pos() / total_height;\n\n        let (min, max) = gesture.min_max(self.workspaces.len());\n        let new_idx = gesture.start_idx + pos;\n\n        let new_idx = new_idx.clamp(min, max);\n        let new_idx = new_idx.round() as usize;\n\n        velocity *= rubber_band.clamp_derivative(min, max, gesture.start_idx + current_pos);\n\n        if self.active_workspace_idx != new_idx {\n            self.previous_workspace_id = Some(self.workspaces[self.active_workspace_idx].id());\n        }\n\n        self.active_workspace_idx = new_idx;\n        self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new(\n            self.clock.clone(),\n            gesture.current_idx,\n            new_idx as f64,\n            velocity,\n            self.options.animations.workspace_switch.0,\n        )));\n\n        true\n    }\n\n    pub fn dnd_scroll_gesture_end(&mut self) {\n        if !matches!(\n            self.workspace_switch,\n            Some(WorkspaceSwitch::Gesture(WorkspaceSwitchGesture {\n                dnd_last_event_time: Some(_),\n                ..\n            }))\n        ) {\n            // Not a DnD scroll.\n            return;\n        };\n\n        self.workspace_switch_gesture_end(None);\n    }\n\n    pub fn scale(&self) -> smithay::output::Scale {\n        self.scale\n    }\n\n    pub fn view_size(&self) -> Size<f64, Logical> {\n        self.view_size\n    }\n\n    pub fn working_area(&self) -> Rectangle<f64, Logical> {\n        self.working_area\n    }\n\n    pub fn layout_config(&self) -> Option<&niri_config::LayoutPart> {\n        self.layout_config.as_ref()\n    }\n\n    #[cfg(test)]\n    pub(super) fn verify_invariants(&self) {\n        use approx::assert_abs_diff_eq;\n\n        let options =\n            Options::clone(&self.base_options).with_merged_layout(self.layout_config.as_ref());\n        assert_eq!(&*self.options, &options);\n\n        assert!(\n            !self.workspaces.is_empty(),\n            \"monitor must have at least one workspace\"\n        );\n        assert!(self.active_workspace_idx < self.workspaces.len());\n\n        if let Some(WorkspaceSwitch::Animation(anim)) = &self.workspace_switch {\n            let before_idx = anim.from() as usize;\n            let after_idx = anim.to() as usize;\n\n            assert!(before_idx < self.workspaces.len());\n            assert!(after_idx < self.workspaces.len());\n        }\n\n        assert!(\n            !self.workspaces.last().unwrap().has_windows(),\n            \"monitor must have an empty workspace in the end\"\n        );\n        if self.options.layout.empty_workspace_above_first {\n            assert!(\n                !self.workspaces.first().unwrap().has_windows(),\n                \"first workspace must be empty when empty_workspace_above_first is set\"\n            )\n        }\n\n        assert!(\n            self.workspaces.last().unwrap().name.is_none(),\n            \"monitor must have an unnamed workspace in the end\"\n        );\n        if self.options.layout.empty_workspace_above_first {\n            assert!(\n                self.workspaces.first().unwrap().name.is_none(),\n                \"first workspace must be unnamed when empty_workspace_above_first is set\"\n            )\n        }\n\n        if self.options.layout.empty_workspace_above_first {\n            assert!(\n                self.workspaces.len() != 2,\n                \"if empty_workspace_above_first is set there must be just 1 or 3+ workspaces\"\n            )\n        }\n\n        // If there's no workspace switch in progress, there can't be any non-last non-active\n        // empty workspaces. If empty_workspace_above_first is set then the first workspace\n        // will be empty too.\n        let pre_skip = if self.options.layout.empty_workspace_above_first {\n            1\n        } else {\n            0\n        };\n        if self.workspace_switch.is_none() {\n            for (idx, ws) in self\n                .workspaces\n                .iter()\n                .enumerate()\n                .skip(pre_skip)\n                .rev()\n                // skip last\n                .skip(1)\n            {\n                if idx != self.active_workspace_idx {\n                    assert!(\n                        ws.has_windows_or_name(),\n                        \"non-active workspace can't be empty and unnamed except the last one\"\n                    );\n                }\n            }\n        }\n\n        for workspace in &self.workspaces {\n            assert_eq!(self.clock, workspace.clock);\n\n            assert_eq!(\n                self.scale().integer_scale(),\n                workspace.scale().integer_scale()\n            );\n            assert_eq!(\n                self.scale().fractional_scale(),\n                workspace.scale().fractional_scale()\n            );\n            assert_eq!(self.view_size, workspace.view_size());\n            assert_eq!(self.working_area, workspace.working_area());\n\n            assert_eq!(\n                workspace.base_options, self.options,\n                \"workspace options must be synchronized with monitor\"\n            );\n        }\n\n        let scale = self.scale().fractional_scale();\n        let iter = self.workspaces_with_render_geo();\n        for (_ws, ws_geo) in iter {\n            let pos = ws_geo.loc;\n            let rounded_pos = pos.to_physical_precise_round(scale).to_logical(scale);\n\n            // Workspace positions must be rounded to physical pixels.\n            assert_abs_diff_eq!(pos.x, rounded_pos.x, epsilon = 1e-5);\n            assert_abs_diff_eq!(pos.y, rounded_pos.y, epsilon = 1e-5);\n        }\n    }\n}\n"
  },
  {
    "path": "src/layout/opening_window.rs",
    "content": "use std::collections::HashMap;\nuse std::rc::Rc;\n\nuse anyhow::Context as _;\nuse glam::{Mat3, Vec2};\nuse smithay::backend::renderer::element::utils::{\n    Relocate, RelocateRenderElement, RescaleRenderElement,\n};\nuse smithay::backend::renderer::element::{Element as _, Kind, RenderElement};\nuse smithay::backend::renderer::gles::{GlesRenderer, Uniform};\nuse smithay::backend::renderer::Texture;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Size};\n\nuse crate::animation::Animation;\nuse crate::niri_render_elements;\nuse crate::render_helpers::offscreen::{OffscreenBuffer, OffscreenData, OffscreenRenderElement};\nuse crate::render_helpers::shader_element::ShaderRenderElement;\nuse crate::render_helpers::shaders::{mat3_uniform, ProgramType, Shaders};\n\n#[derive(Debug)]\npub struct OpenAnimation {\n    anim: Animation,\n    random_seed: f32,\n    buffer: OffscreenBuffer,\n}\n\nniri_render_elements! {\n    OpeningWindowRenderElement => {\n        Offscreen = RelocateRenderElement<RescaleRenderElement<OffscreenRenderElement>>,\n        Shader = ShaderRenderElement,\n    }\n}\n\nimpl OpenAnimation {\n    pub fn new(anim: Animation) -> Self {\n        Self {\n            anim,\n            random_seed: fastrand::f32(),\n            buffer: OffscreenBuffer::default(),\n        }\n    }\n\n    pub fn is_done(&self) -> bool {\n        self.anim.is_done()\n    }\n\n    // We can't depend on view_rect here, because the result of window opening can be snapshot and\n    // then rendered elsewhere.\n    pub fn render(\n        &self,\n        renderer: &mut GlesRenderer,\n        elements: &[impl RenderElement<GlesRenderer>],\n        geo_size: Size<f64, Logical>,\n        location: Point<f64, Logical>,\n        scale: Scale<f64>,\n        alpha: f32,\n    ) -> anyhow::Result<(OpeningWindowRenderElement, OffscreenData)> {\n        let progress = self.anim.value();\n        let clamped_progress = self.anim.clamped_value().clamp(0., 1.);\n\n        let (elem, _sync_point, mut data) = self\n            .buffer\n            .render(renderer, scale, elements)\n            .context(\"error rendering to offscreen buffer\")?;\n\n        if Shaders::get(renderer).program(ProgramType::Open).is_some() {\n            // OffscreenBuffer renders with Transform::Normal and the scale that we passed, so we\n            // can assume that below.\n            let offset = elem.offset();\n            let texture = elem.texture();\n            let texture_size = elem.logical_size();\n\n            let mut area = Rectangle::new(location + offset, texture_size);\n\n            // Expand the area a bit to allow for more varied effects.\n            let mut target_size = area.size.upscale(1.5);\n            target_size.w = f64::max(area.size.w + 1000., target_size.w);\n            target_size.h = f64::max(area.size.h + 1000., target_size.h);\n            let diff = (target_size.to_point() - area.size.to_point()).downscale(2.);\n            let diff = diff.to_physical_precise_round(scale).to_logical(scale);\n            area.loc -= diff;\n            area.size += diff.upscale(2.).to_size();\n\n            let area_loc = Vec2::new(area.loc.x as f32, area.loc.y as f32);\n            let area_size = Vec2::new(area.size.w as f32, area.size.h as f32);\n\n            let geo_loc = Vec2::new(location.x as f32, location.y as f32);\n            let geo_size = Vec2::new(geo_size.w as f32, geo_size.h as f32);\n\n            let input_to_geo = Mat3::from_scale(area_size / geo_size)\n                * Mat3::from_translation((area_loc - geo_loc) / area_size);\n\n            let tex_scale = Vec2::new(scale.x as f32, scale.y as f32);\n            let tex_loc = Vec2::new(offset.x as f32, offset.y as f32);\n            let tex_size = Vec2::new(texture.width() as f32, texture.height() as f32) / tex_scale;\n\n            let geo_to_tex =\n                Mat3::from_translation(-tex_loc / tex_size) * Mat3::from_scale(geo_size / tex_size);\n\n            let elem = ShaderRenderElement::new(\n                ProgramType::Open,\n                area.size,\n                None,\n                scale.x as f32,\n                alpha,\n                Rc::new([\n                    mat3_uniform(\"niri_input_to_geo\", input_to_geo),\n                    Uniform::new(\"niri_geo_size\", geo_size.to_array()),\n                    mat3_uniform(\"niri_geo_to_tex\", geo_to_tex),\n                    Uniform::new(\"niri_progress\", progress as f32),\n                    Uniform::new(\"niri_clamped_progress\", clamped_progress as f32),\n                    Uniform::new(\"niri_random_seed\", self.random_seed),\n                ]),\n                HashMap::from([(String::from(\"niri_tex\"), texture.clone())]),\n                Kind::Unspecified,\n            )\n            .with_location(area.loc);\n\n            // We're drawing the shader, not the offscreen itself.\n            data.id = elem.id().clone();\n\n            return Ok((elem.into(), data));\n        }\n\n        let elem = elem.with_alpha(clamped_progress as f32 * alpha);\n\n        let center = geo_size.to_point().downscale(2.);\n        let elem = RescaleRenderElement::from_element(\n            elem,\n            center.to_physical_precise_round(scale),\n            (progress / 2. + 0.5).max(0.),\n        );\n\n        let elem = RelocateRenderElement::from_element(\n            elem,\n            location.to_physical_precise_round(scale),\n            Relocate::Relative,\n        );\n\n        Ok((elem.into(), data))\n    }\n}\n"
  },
  {
    "path": "src/layout/scrolling.rs",
    "content": "use std::cmp::{max, min};\nuse std::iter::{self, zip};\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse niri_config::utils::MergeWith as _;\nuse niri_config::{CenterFocusedColumn, PresetSize, Struts};\nuse niri_ipc::{ColumnDisplay, SizeChange, WindowLayout};\nuse ordered_float::NotNan;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size};\n\nuse super::closing_window::{ClosingWindow, ClosingWindowRenderElement};\nuse super::monitor::InsertPosition;\nuse super::tab_indicator::{TabIndicator, TabIndicatorRenderElement, TabInfo};\nuse super::tile::{Tile, TileRenderElement, TileRenderSnapshot};\nuse super::workspace::{InteractiveResize, ResolvedSize};\nuse super::{ConfigureIntent, HitType, InteractiveResizeData, LayoutElement, Options, RemovedTile};\nuse crate::animation::{Animation, Clock};\nuse crate::input::swipe_tracker::SwipeTracker;\nuse crate::layout::SizingMode;\nuse crate::niri_render_elements;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::RenderTarget;\nuse crate::utils::transaction::{Transaction, TransactionBlocker};\nuse crate::utils::ResizeEdge;\nuse crate::window::ResolvedWindowRules;\n\n/// Amount of touchpad movement to scroll the view for the width of one working area.\nconst VIEW_GESTURE_WORKING_AREA_MOVEMENT: f64 = 1200.;\n\n/// A scrollable-tiling space for windows.\n#[derive(Debug)]\npub struct ScrollingSpace<W: LayoutElement> {\n    /// Columns of windows on this space.\n    columns: Vec<Column<W>>,\n\n    /// Extra per-column data.\n    data: Vec<ColumnData>,\n\n    /// Index of the currently active column, if any.\n    active_column_idx: usize,\n\n    /// Ongoing interactive resize.\n    interactive_resize: Option<InteractiveResize<W>>,\n\n    /// Offset of the view computed from the active column.\n    ///\n    /// Any gaps, including left padding from work area left exclusive zone, is handled\n    /// with this view offset (rather than added as a constant elsewhere in the code). This allows\n    /// for natural handling of fullscreen windows, which must ignore work area padding.\n    view_offset: ViewOffset,\n\n    /// Whether to activate the previous, rather than the next, column upon column removal.\n    ///\n    /// When a new column is created and removed with no focus changes in-between, it is more\n    /// natural to activate the previously-focused column. This variable tracks that.\n    ///\n    /// Since we only create-and-activate columns immediately to the right of the active column (in\n    /// contrast to tabs in Firefox, for example), we can track this as a bool, rather than an\n    /// index of the previous column to activate.\n    ///\n    /// The value is the view offset that the previous column had before, to restore it.\n    activate_prev_column_on_removal: Option<f64>,\n\n    /// View offset to restore after unfullscreening or unmaximizing.\n    view_offset_to_restore: Option<f64>,\n\n    /// Windows in the closing animation.\n    closing_windows: Vec<ClosingWindow>,\n\n    /// View size for this space.\n    view_size: Size<f64, Logical>,\n\n    /// Working area for this space.\n    ///\n    /// Takes into account layer-shell exclusive zones and niri struts.\n    working_area: Rectangle<f64, Logical>,\n\n    /// Working area for this space excluding struts.\n    ///\n    /// Used for popup unconstraining. Popups can go over struts, but they shouldn't go over\n    /// the layer-shell top layer (which renders on top of popups).\n    parent_area: Rectangle<f64, Logical>,\n\n    /// Scale of the output the space is on (and rounds its sizes to).\n    scale: f64,\n\n    /// Clock for driving animations.\n    clock: Clock,\n\n    /// Configurable properties of the layout.\n    options: Rc<Options>,\n}\n\nniri_render_elements! {\n    ScrollingSpaceRenderElement<R> => {\n        Tile = TileRenderElement<R>,\n        ClosingWindow = ClosingWindowRenderElement,\n        TabIndicator = TabIndicatorRenderElement,\n    }\n}\n\n/// Extra per-column data.\n#[derive(Debug, Clone, Copy, PartialEq)]\nstruct ColumnData {\n    /// Cached actual column width.\n    width: f64,\n}\n\n#[derive(Debug)]\npub(super) enum ViewOffset {\n    /// The view offset is static.\n    Static(f64),\n    /// The view offset is animating.\n    Animation(Animation),\n    /// The view offset is controlled by the ongoing gesture.\n    Gesture(ViewGesture),\n}\n\n#[derive(Debug)]\npub(super) struct ViewGesture {\n    current_view_offset: f64,\n    /// Animation for the extra offset to the current position.\n    ///\n    /// For example, when we need to activate a specific window during a DnD scroll.\n    animation: Option<Animation>,\n    tracker: SwipeTracker,\n    delta_from_tracker: f64,\n    // The view offset we'll use if needed for activate_prev_column_on_removal.\n    stationary_view_offset: f64,\n    /// Whether the gesture is controlled by the touchpad.\n    is_touchpad: bool,\n\n    // If this gesture is for drag-and-drop scrolling, this is the last event's unadjusted\n    // timestamp.\n    dnd_last_event_time: Option<Duration>,\n    // Time when the drag-and-drop scroll delta became non-zero, used for debouncing.\n    //\n    // If `None` then the scroll delta is currently zero.\n    dnd_nonzero_start_time: Option<Duration>,\n}\n\n#[derive(Debug)]\npub struct Column<W: LayoutElement> {\n    /// Tiles in this column.\n    ///\n    /// Must be non-empty.\n    tiles: Vec<Tile<W>>,\n\n    /// Extra per-tile data.\n    ///\n    /// Must have the same number of elements as `tiles`.\n    data: Vec<TileData>,\n\n    /// Index of the currently active tile.\n    active_tile_idx: usize,\n\n    /// Desired width of this column.\n    ///\n    /// If the column is full-width or full-screened, this is the width that should be restored\n    /// upon unfullscreening and untoggling full-width.\n    width: ColumnWidth,\n\n    /// Currently selected preset width index.\n    preset_width_idx: Option<usize>,\n\n    /// Whether this column is full-width.\n    is_full_width: bool,\n\n    /// Whether this column is going to be fullscreen.\n    ///\n    /// This is the compositor-side fullscreen state, so it changes immediately upon\n    /// set_fullscreen(). The actual tiles will take some time to respond to the fullscreen request\n    /// and become fullscreen.\n    ///\n    /// Similarly, unsetting fullscreen will change this value to false immediately, and tiles will\n    /// take some time to catch up and actually unfullscreen.\n    is_pending_fullscreen: bool,\n\n    /// Whether this column is going to be maximized.\n    ///\n    /// Can be `true` together with `is_pending_fullscreen`, which means that the column is\n    /// effectively pending fullscreen, but unfullscreening should go back to maximized state,\n    /// rather than normal.\n    is_pending_maximized: bool,\n\n    /// How this column displays and arranges windows.\n    display_mode: ColumnDisplay,\n\n    /// Tab indicator for the tabbed display mode.\n    tab_indicator: TabIndicator,\n\n    /// Animation of the render offset during window swapping.\n    move_animation: Option<MoveAnimation>,\n\n    /// Latest known view size for this column's workspace.\n    view_size: Size<f64, Logical>,\n\n    /// Latest known working area for this column's workspace.\n    working_area: Rectangle<f64, Logical>,\n\n    /// Working area for this column's workspace excluding struts.\n    ///\n    /// Used for maximize-to-edges.\n    parent_area: Rectangle<f64, Logical>,\n\n    /// Scale of the output the column is on (and rounds its sizes to).\n    scale: f64,\n\n    /// Clock for driving animations.\n    clock: Clock,\n\n    /// Configurable properties of the layout.\n    options: Rc<Options>,\n}\n\n/// Extra per-tile data.\n#[derive(Debug, Clone, Copy, PartialEq)]\nstruct TileData {\n    /// Requested height of the window.\n    ///\n    /// This is window height, not tile height, so it excludes tile decorations.\n    height: WindowHeight,\n\n    /// Cached actual size of the tile.\n    size: Size<f64, Logical>,\n\n    /// Cached whether the tile is being interactively resized by its left edge.\n    interactively_resizing_by_left_edge: bool,\n}\n\n/// Width of a column.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum ColumnWidth {\n    /// Proportion of the current view width.\n    Proportion(f64),\n    /// Fixed width in logical pixels.\n    Fixed(f64),\n}\n\n/// Height of a window in a column.\n///\n/// Every window but one in a column must be `Auto`-sized so that the total height can add up to\n/// the workspace height. Resizing a window converts all other windows to `Auto`, weighted to\n/// preserve their visual heights at the moment of the conversion.\n///\n/// In contrast to column widths, proportional height changes are converted to, and stored as,\n/// fixed height right away. With column widths you frequently want e.g. two columns side-by-side\n/// with 50% width each, and you want them to remain this way when moving to a differently sized\n/// monitor. Windows in a column, however, already auto-size to fill the available height, giving\n/// you this behavior. The main reason to set a different window height, then, is when you want\n/// something in the window to fit exactly, e.g. to fit 30 lines in a terminal, which corresponds\n/// to the `Fixed` variant.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum WindowHeight {\n    /// Automatically computed *tile* height, distributed across the column according to weights.\n    ///\n    /// This controls the tile height rather than the window height because it's easier in the auto\n    /// height distribution algorithm.\n    Auto { weight: f64 },\n    /// Fixed *window* height in logical pixels.\n    Fixed(f64),\n    /// One of the preset heights (tile or window).\n    Preset(usize),\n}\n\n/// Horizontal direction for an operation.\n///\n/// As operations often have a symmetrical counterpart, e.g. focus-right/focus-left, methods\n/// on `Scrolling` can sometimes be factored using the direction of the operation as a parameter.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum ScrollDirection {\n    Left,\n    Right,\n}\n\n#[derive(Debug)]\nstruct MoveAnimation {\n    anim: Animation,\n    from: f64,\n}\n\nimpl<W: LayoutElement> ScrollingSpace<W> {\n    pub fn new(\n        view_size: Size<f64, Logical>,\n        parent_area: Rectangle<f64, Logical>,\n        scale: f64,\n        clock: Clock,\n        options: Rc<Options>,\n    ) -> Self {\n        let working_area = compute_working_area(parent_area, scale, options.layout.struts);\n\n        Self {\n            columns: Vec::new(),\n            data: Vec::new(),\n            active_column_idx: 0,\n            interactive_resize: None,\n            view_offset: ViewOffset::Static(0.),\n            activate_prev_column_on_removal: None,\n            view_offset_to_restore: None,\n            closing_windows: Vec::new(),\n            view_size,\n            working_area,\n            parent_area,\n            scale,\n            clock,\n            options,\n        }\n    }\n\n    pub fn update_config(\n        &mut self,\n        view_size: Size<f64, Logical>,\n        parent_area: Rectangle<f64, Logical>,\n        scale: f64,\n        options: Rc<Options>,\n    ) {\n        let working_area = compute_working_area(parent_area, scale, options.layout.struts);\n\n        for (column, data) in zip(&mut self.columns, &mut self.data) {\n            column.update_config(view_size, working_area, parent_area, scale, options.clone());\n            data.update(column);\n        }\n\n        self.view_size = view_size;\n        self.working_area = working_area;\n        self.parent_area = parent_area;\n        self.scale = scale;\n        self.options = options;\n\n        // Apply always-center and such right away.\n        if !self.columns.is_empty() && !self.view_offset.is_gesture() {\n            self.animate_view_offset_to_column(None, self.active_column_idx, None);\n        }\n    }\n\n    pub fn update_shaders(&mut self) {\n        for col in &mut self.columns {\n            col.update_shaders();\n        }\n    }\n\n    pub fn advance_animations(&mut self) {\n        if let ViewOffset::Animation(anim) = &self.view_offset {\n            if anim.is_done() {\n                self.view_offset = ViewOffset::Static(anim.to());\n            }\n        }\n\n        if let ViewOffset::Gesture(gesture) = &mut self.view_offset {\n            // Make sure the last event time doesn't go too much out of date (for\n            // workspaces not under cursor), causing sudden jumps.\n            //\n            // This happens after any dnd_scroll_gesture_scroll() calls (in\n            // Layout::advance_animations()), so it doesn't mess up the time delta there.\n            if let Some(last_time) = &mut gesture.dnd_last_event_time {\n                let now = self.clock.now_unadjusted();\n                if *last_time != now {\n                    *last_time = now;\n\n                    // If last_time was already == now, then dnd_scroll_gesture_scroll() must've\n                    // updated the gesture already. Therefore, when this code runs, the pointer\n                    // must be outside the DnD scrolling zone.\n                    gesture.dnd_nonzero_start_time = None;\n                }\n            }\n\n            if let Some(anim) = &mut gesture.animation {\n                if anim.is_done() {\n                    gesture.animation = None;\n                }\n            }\n        }\n\n        for col in &mut self.columns {\n            col.advance_animations();\n        }\n\n        self.closing_windows.retain_mut(|closing| {\n            closing.advance_animations();\n            closing.are_animations_ongoing()\n        });\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        self.view_offset.is_animation_ongoing()\n            || self.columns.iter().any(Column::are_animations_ongoing)\n            || !self.closing_windows.is_empty()\n    }\n\n    pub fn are_transitions_ongoing(&self) -> bool {\n        !self.view_offset.is_static()\n            || self.columns.iter().any(Column::are_transitions_ongoing)\n            || !self.closing_windows.is_empty()\n    }\n\n    pub fn update_render_elements(&mut self, is_active: bool) {\n        let view_pos = Point::from((self.view_pos(), 0.));\n        let view_size = self.view_size;\n        let active_idx = self.active_column_idx;\n        for (col_idx, (col, col_x)) in self.columns_mut().enumerate() {\n            let is_active = is_active && col_idx == active_idx;\n            let col_off = Point::from((col_x, 0.));\n            let col_pos = view_pos - col_off - col.render_offset();\n            let view_rect = Rectangle::new(col_pos, view_size);\n            col.update_render_elements(is_active, view_rect);\n        }\n    }\n\n    pub fn tiles(&self) -> impl Iterator<Item = &Tile<W>> + '_ {\n        self.columns.iter().flat_map(|col| col.tiles.iter())\n    }\n\n    pub fn tiles_mut(&mut self) -> impl Iterator<Item = &mut Tile<W>> + '_ {\n        self.columns.iter_mut().flat_map(|col| col.tiles.iter_mut())\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.columns.is_empty()\n    }\n\n    pub fn active_window(&self) -> Option<&W> {\n        if self.columns.is_empty() {\n            return None;\n        }\n\n        let col = &self.columns[self.active_column_idx];\n        Some(col.tiles[col.active_tile_idx].window())\n    }\n\n    pub fn active_window_mut(&mut self) -> Option<&mut W> {\n        if self.columns.is_empty() {\n            return None;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        Some(col.tiles[col.active_tile_idx].window_mut())\n    }\n\n    pub fn active_tile_mut(&mut self) -> Option<&mut Tile<W>> {\n        if self.columns.is_empty() {\n            return None;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        Some(&mut col.tiles[col.active_tile_idx])\n    }\n\n    pub fn is_active_pending_fullscreen(&self) -> bool {\n        if self.columns.is_empty() {\n            return false;\n        }\n\n        let col = &self.columns[self.active_column_idx];\n        col.pending_sizing_mode().is_fullscreen()\n    }\n\n    pub fn new_window_toplevel_bounds(&self, rules: &ResolvedWindowRules) -> Size<i32, Logical> {\n        let border_config = self.options.layout.border.merged_with(&rules.border);\n\n        let display_mode = rules\n            .default_column_display\n            .unwrap_or(self.options.layout.default_column_display);\n        let will_tab = display_mode == ColumnDisplay::Tabbed;\n        let extra_size = if will_tab {\n            TabIndicator::new(self.options.layout.tab_indicator).extra_size(1, self.scale)\n        } else {\n            Size::from((0., 0.))\n        };\n\n        compute_toplevel_bounds(\n            border_config,\n            self.working_area.size,\n            extra_size,\n            self.options.layout.gaps,\n        )\n    }\n\n    pub fn new_window_size(\n        &self,\n        width: Option<PresetSize>,\n        height: Option<PresetSize>,\n        rules: &ResolvedWindowRules,\n    ) -> Size<i32, Logical> {\n        let border = self.options.layout.border.merged_with(&rules.border);\n\n        let display_mode = rules\n            .default_column_display\n            .unwrap_or(self.options.layout.default_column_display);\n        let will_tab = display_mode == ColumnDisplay::Tabbed;\n        let extra = if will_tab {\n            TabIndicator::new(self.options.layout.tab_indicator).extra_size(1, self.scale)\n        } else {\n            Size::from((0., 0.))\n        };\n\n        let working_size = self.working_area.size;\n\n        let width = if let Some(size) = width {\n            let size = match resolve_preset_size(size, &self.options, working_size.w, extra.w) {\n                ResolvedSize::Tile(mut size) => {\n                    if !border.off {\n                        size -= border.width * 2.;\n                    }\n                    size\n                }\n                ResolvedSize::Window(size) => size,\n            };\n\n            max(1, size.floor() as i32)\n        } else {\n            0\n        };\n\n        let mut full_height = self.working_area.size.h - self.options.layout.gaps * 2.;\n        if !border.off {\n            full_height -= border.width * 2.;\n        }\n\n        let height = if let Some(height) = height {\n            let height = match resolve_preset_size(height, &self.options, working_size.h, extra.h) {\n                ResolvedSize::Tile(mut size) => {\n                    if !border.off {\n                        size -= border.width * 2.;\n                    }\n                    size\n                }\n                ResolvedSize::Window(size) => size,\n            };\n            f64::min(height, full_height)\n        } else {\n            full_height\n        };\n\n        Size::from((width, max(height.floor() as i32, 1)))\n    }\n\n    pub fn is_centering_focused_column(&self) -> bool {\n        self.options.layout.center_focused_column == CenterFocusedColumn::Always\n            || (self.options.layout.always_center_single_column && self.columns.len() <= 1)\n    }\n\n    fn compute_new_view_offset_fit(\n        &self,\n        target_x: Option<f64>,\n        col_x: f64,\n        width: f64,\n        mode: SizingMode,\n    ) -> f64 {\n        if mode.is_fullscreen() {\n            return 0.;\n        }\n\n        let (area, padding) = if mode.is_maximized() {\n            (self.parent_area, 0.)\n        } else {\n            (self.working_area, self.options.layout.gaps)\n        };\n\n        let target_x = target_x.unwrap_or_else(|| self.target_view_pos());\n\n        let new_offset =\n            compute_new_view_offset(target_x + area.loc.x, area.size.w, col_x, width, padding);\n\n        // Non-fullscreen windows are always offset at least by the working area position.\n        new_offset - area.loc.x\n    }\n\n    fn compute_new_view_offset_centered(\n        &self,\n        target_x: Option<f64>,\n        col_x: f64,\n        width: f64,\n        mode: SizingMode,\n    ) -> f64 {\n        if mode.is_fullscreen() {\n            return self.compute_new_view_offset_fit(target_x, col_x, width, mode);\n        }\n\n        let area = if mode.is_maximized() {\n            self.parent_area\n        } else {\n            self.working_area\n        };\n\n        // Columns wider than the view are left-aligned (the fit code can deal with that).\n        if area.size.w <= width {\n            return self.compute_new_view_offset_fit(target_x, col_x, width, mode);\n        }\n\n        -(area.size.w - width) / 2. - area.loc.x\n    }\n\n    fn compute_new_view_offset_for_column_fit(&self, target_x: Option<f64>, idx: usize) -> f64 {\n        let col = &self.columns[idx];\n        self.compute_new_view_offset_fit(\n            target_x,\n            self.column_x(idx),\n            col.width(),\n            col.sizing_mode(),\n        )\n    }\n\n    fn compute_new_view_offset_for_column_centered(\n        &self,\n        target_x: Option<f64>,\n        idx: usize,\n    ) -> f64 {\n        let col = &self.columns[idx];\n        self.compute_new_view_offset_centered(\n            target_x,\n            self.column_x(idx),\n            col.width(),\n            col.sizing_mode(),\n        )\n    }\n\n    fn compute_new_view_offset_for_column(\n        &self,\n        target_x: Option<f64>,\n        idx: usize,\n        prev_idx: Option<usize>,\n    ) -> f64 {\n        if self.is_centering_focused_column() {\n            return self.compute_new_view_offset_for_column_centered(target_x, idx);\n        }\n\n        match self.options.layout.center_focused_column {\n            CenterFocusedColumn::Always => {\n                self.compute_new_view_offset_for_column_centered(target_x, idx)\n            }\n            CenterFocusedColumn::OnOverflow => {\n                let Some(prev_idx) = prev_idx else {\n                    return self.compute_new_view_offset_for_column_fit(target_x, idx);\n                };\n\n                // Activating the same column.\n                if prev_idx == idx {\n                    return self.compute_new_view_offset_for_column_fit(target_x, idx);\n                }\n\n                // Always take the left or right neighbor of the target as the source.\n                let source_idx = if prev_idx > idx {\n                    min(idx + 1, self.columns.len() - 1)\n                } else {\n                    idx.saturating_sub(1)\n                };\n\n                let source_col_x = self.column_x(source_idx);\n                let source_col_width = self.columns[source_idx].width();\n\n                let target_col_x = self.column_x(idx);\n                let target_col_width = self.columns[idx].width();\n\n                // NOTE: This logic won't work entirely correctly with small fixed-size maximized\n                // windows (they have a different area and padding).\n                let total_width = if source_col_x < target_col_x {\n                    // Source is left from target.\n                    target_col_x - source_col_x + target_col_width\n                } else {\n                    // Source is right from target.\n                    source_col_x - target_col_x + source_col_width\n                } + self.options.layout.gaps * 2.;\n\n                // If it fits together, do a normal animation, otherwise center the new column.\n                if total_width <= self.working_area.size.w {\n                    self.compute_new_view_offset_for_column_fit(target_x, idx)\n                } else {\n                    self.compute_new_view_offset_for_column_centered(target_x, idx)\n                }\n            }\n            CenterFocusedColumn::Never => {\n                self.compute_new_view_offset_for_column_fit(target_x, idx)\n            }\n        }\n    }\n\n    fn animate_view_offset(&mut self, idx: usize, new_view_offset: f64) {\n        self.animate_view_offset_with_config(\n            idx,\n            new_view_offset,\n            self.options.animations.horizontal_view_movement.0,\n        );\n    }\n\n    fn animate_view_offset_with_config(\n        &mut self,\n        idx: usize,\n        new_view_offset: f64,\n        config: niri_config::Animation,\n    ) {\n        let new_col_x = self.column_x(idx);\n        let old_col_x = self.column_x(self.active_column_idx);\n        let offset_delta = old_col_x - new_col_x;\n        self.view_offset.offset(offset_delta);\n\n        let pixel = 1. / self.scale;\n\n        // If our view offset is already this or animating towards this, we don't need to do\n        // anything.\n        let to_diff = new_view_offset - self.view_offset.target();\n        if to_diff.abs() < pixel {\n            // Correct for any inaccuracy.\n            self.view_offset.offset(to_diff);\n            return;\n        }\n\n        match &mut self.view_offset {\n            ViewOffset::Gesture(gesture) if gesture.dnd_last_event_time.is_some() => {\n                gesture.stationary_view_offset = new_view_offset;\n\n                let current_pos = gesture.current_view_offset - gesture.delta_from_tracker;\n                gesture.delta_from_tracker = new_view_offset - current_pos;\n                let offset_delta = new_view_offset - gesture.current_view_offset;\n                gesture.current_view_offset = new_view_offset;\n\n                gesture.animate_from(-offset_delta, self.clock.clone(), config);\n            }\n            _ => {\n                // FIXME: also compute and use current velocity.\n                self.view_offset = ViewOffset::Animation(Animation::new(\n                    self.clock.clone(),\n                    self.view_offset.current(),\n                    new_view_offset,\n                    0.,\n                    config,\n                ));\n            }\n        }\n    }\n\n    fn animate_view_offset_to_column_centered(\n        &mut self,\n        target_x: Option<f64>,\n        idx: usize,\n        config: niri_config::Animation,\n    ) {\n        let new_view_offset = self.compute_new_view_offset_for_column_centered(target_x, idx);\n        self.animate_view_offset_with_config(idx, new_view_offset, config);\n    }\n\n    fn animate_view_offset_to_column_with_config(\n        &mut self,\n        target_x: Option<f64>,\n        idx: usize,\n        prev_idx: Option<usize>,\n        config: niri_config::Animation,\n    ) {\n        let new_view_offset = self.compute_new_view_offset_for_column(target_x, idx, prev_idx);\n        self.animate_view_offset_with_config(idx, new_view_offset, config);\n    }\n\n    fn animate_view_offset_to_column(\n        &mut self,\n        target_x: Option<f64>,\n        idx: usize,\n        prev_idx: Option<usize>,\n    ) {\n        self.animate_view_offset_to_column_with_config(\n            target_x,\n            idx,\n            prev_idx,\n            self.options.animations.horizontal_view_movement.0,\n        )\n    }\n\n    fn activate_column(&mut self, idx: usize) {\n        self.activate_column_with_anim_config(\n            idx,\n            self.options.animations.horizontal_view_movement.0,\n        );\n    }\n\n    fn activate_column_with_anim_config(&mut self, idx: usize, config: niri_config::Animation) {\n        if self.active_column_idx == idx\n            // During a DnD scroll, animate even when activating the same window, for DnD hold.\n            && (self.columns.is_empty() || !self.view_offset.is_dnd_scroll())\n        {\n            return;\n        }\n\n        self.animate_view_offset_to_column_with_config(\n            None,\n            idx,\n            Some(self.active_column_idx),\n            config,\n        );\n\n        if self.active_column_idx != idx {\n            self.active_column_idx = idx;\n\n            // A different column was activated; reset the flag.\n            self.activate_prev_column_on_removal = None;\n            self.view_offset_to_restore = None;\n            self.interactive_resize = None;\n        }\n    }\n\n    pub(super) fn insert_position(&self, pos: Point<f64, Logical>) -> InsertPosition {\n        if self.columns.is_empty() {\n            return InsertPosition::NewColumn(0);\n        }\n\n        let x = pos.x + self.view_pos();\n\n        // Aim for the center of the gap.\n        let x = x + self.options.layout.gaps / 2.;\n        let y = pos.y + self.options.layout.gaps / 2.;\n\n        // Insert position is before the first column.\n        if x < 0. {\n            return InsertPosition::NewColumn(0);\n        }\n\n        // Find the closest gap between columns.\n        let (closest_col_idx, col_x) = self\n            .column_xs(self.data.iter().copied())\n            .enumerate()\n            .min_by_key(|(_, col_x)| NotNan::new((col_x - x).abs()).unwrap())\n            .unwrap();\n\n        // Find the column containing the position.\n        let (col_idx, _) = self\n            .column_xs(self.data.iter().copied())\n            .enumerate()\n            .take_while(|(_, col_x)| *col_x <= x)\n            .last()\n            .unwrap_or((0, 0.));\n\n        // Insert position is past the last column.\n        if col_idx == self.columns.len() {\n            return InsertPosition::NewColumn(closest_col_idx);\n        }\n\n        // Find the closest gap between tiles.\n        let col = &self.columns[col_idx];\n\n        let (closest_tile_idx, tile_y) = if col.display_mode == ColumnDisplay::Tabbed {\n            // In tabbed mode, there's only one tile visible, and we want to check its top and\n            // bottom.\n            let top = col.tile_offsets().nth(col.active_tile_idx).unwrap().y;\n            let bottom = top + col.data[col.active_tile_idx].size.h;\n            if (top - y).abs() <= (bottom - y).abs() {\n                (col.active_tile_idx, top)\n            } else {\n                (col.active_tile_idx + 1, bottom)\n            }\n        } else {\n            col.tile_offsets()\n                .map(|tile_off| tile_off.y)\n                .enumerate()\n                .min_by_key(|(_, tile_y)| NotNan::new((tile_y - y).abs()).unwrap())\n                .unwrap()\n        };\n\n        // Return the closest among the vertical and the horizontal gap.\n        let vert_dist = (col_x - x).abs();\n        let hor_dist = (tile_y - y).abs();\n        if vert_dist <= hor_dist {\n            InsertPosition::NewColumn(closest_col_idx)\n        } else {\n            InsertPosition::InColumn(col_idx, closest_tile_idx)\n        }\n    }\n\n    pub fn add_tile(\n        &mut self,\n        col_idx: Option<usize>,\n        tile: Tile<W>,\n        activate: bool,\n        width: ColumnWidth,\n        is_full_width: bool,\n        anim_config: Option<niri_config::Animation>,\n    ) {\n        let column = Column::new_with_tile(\n            tile,\n            self.view_size,\n            self.working_area,\n            self.parent_area,\n            self.scale,\n            width,\n            is_full_width,\n        );\n\n        self.add_column(col_idx, column, activate, anim_config);\n    }\n\n    pub fn add_tile_to_column(\n        &mut self,\n        col_idx: usize,\n        tile_idx: Option<usize>,\n        tile: Tile<W>,\n        activate: bool,\n    ) {\n        let prev_next_x = self.column_x(col_idx + 1);\n\n        let target_column = &mut self.columns[col_idx];\n        let tile_idx = tile_idx.unwrap_or(target_column.tiles.len());\n        let mut prev_active_tile_idx = target_column.active_tile_idx;\n\n        target_column.add_tile_at(tile_idx, tile);\n        self.data[col_idx].update(target_column);\n\n        if tile_idx <= prev_active_tile_idx {\n            target_column.active_tile_idx += 1;\n            prev_active_tile_idx += 1;\n        }\n\n        if activate {\n            target_column.activate_idx(tile_idx);\n            if self.active_column_idx != col_idx {\n                self.activate_column(col_idx);\n            }\n        }\n\n        let target_column = &mut self.columns[col_idx];\n        if target_column.display_mode == ColumnDisplay::Tabbed {\n            if target_column.active_tile_idx == tile_idx {\n                // Fade out the previously active tile.\n                let tile = &mut target_column.tiles[prev_active_tile_idx];\n                tile.animate_alpha(1., 0., self.options.animations.window_movement.0);\n            } else {\n                // Fade out when adding into a tabbed column into the background.\n                let tile = &mut target_column.tiles[tile_idx];\n                tile.animate_alpha(1., 0., self.options.animations.window_movement.0);\n            }\n        }\n\n        // Adding a wider window into a column increases its width now (even if the window will\n        // shrink later). Move the columns to account for this.\n        let offset = self.column_x(col_idx + 1) - prev_next_x;\n        if self.active_column_idx <= col_idx {\n            for col in &mut self.columns[col_idx + 1..] {\n                col.animate_move_from(-offset);\n            }\n        } else {\n            for col in &mut self.columns[..=col_idx] {\n                col.animate_move_from(offset);\n            }\n        }\n    }\n\n    pub fn add_tile_right_of(\n        &mut self,\n        right_of: &W::Id,\n        tile: Tile<W>,\n        activate: bool,\n        width: ColumnWidth,\n        is_full_width: bool,\n    ) {\n        let right_of_idx = self\n            .columns\n            .iter()\n            .position(|col| col.contains(right_of))\n            .unwrap();\n        let col_idx = right_of_idx + 1;\n\n        self.add_tile(Some(col_idx), tile, activate, width, is_full_width, None);\n    }\n\n    pub fn add_column(\n        &mut self,\n        idx: Option<usize>,\n        mut column: Column<W>,\n        activate: bool,\n        anim_config: Option<niri_config::Animation>,\n    ) {\n        let was_empty = self.columns.is_empty();\n\n        let idx = idx.unwrap_or_else(|| {\n            if was_empty {\n                0\n            } else {\n                self.active_column_idx + 1\n            }\n        });\n\n        column.update_config(\n            self.view_size,\n            self.working_area,\n            self.parent_area,\n            self.scale,\n            self.options.clone(),\n        );\n        self.data.insert(idx, ColumnData::new(&column));\n        self.columns.insert(idx, column);\n\n        if !was_empty && idx <= self.active_column_idx {\n            self.active_column_idx += 1;\n        }\n\n        // Animate movement of other columns.\n        let offset = self.column_x(idx + 1) - self.column_x(idx);\n        let config = anim_config.unwrap_or(self.options.animations.window_movement.0);\n        if self.active_column_idx <= idx {\n            for col in &mut self.columns[idx + 1..] {\n                col.animate_move_from_with_config(-offset, config);\n            }\n        } else {\n            for col in &mut self.columns[..idx] {\n                col.animate_move_from_with_config(offset, config);\n            }\n        }\n\n        if activate {\n            // If this is the first window on an empty workspace, remove the effect of whatever\n            // view_offset was left over and skip the animation.\n            if was_empty {\n                self.view_offset = ViewOffset::Static(0.);\n                self.view_offset =\n                    ViewOffset::Static(self.compute_new_view_offset_for_column(None, idx, None));\n            }\n\n            let prev_offset = (!was_empty && idx == self.active_column_idx + 1)\n                .then(|| self.view_offset.stationary());\n\n            let anim_config =\n                anim_config.unwrap_or(self.options.animations.horizontal_view_movement.0);\n            self.activate_column_with_anim_config(idx, anim_config);\n            self.activate_prev_column_on_removal = prev_offset;\n        }\n    }\n\n    pub fn remove_active_tile(&mut self, transaction: Transaction) -> Option<RemovedTile<W>> {\n        if self.columns.is_empty() {\n            return None;\n        }\n\n        let column = &self.columns[self.active_column_idx];\n        Some(self.remove_tile_by_idx(\n            self.active_column_idx,\n            column.active_tile_idx,\n            transaction,\n            None,\n        ))\n    }\n\n    pub fn remove_tile(&mut self, window: &W::Id, transaction: Transaction) -> RemovedTile<W> {\n        let column_idx = self\n            .columns\n            .iter()\n            .position(|col| col.contains(window))\n            .unwrap();\n        let column = &self.columns[column_idx];\n\n        let tile_idx = column.position(window).unwrap();\n        self.remove_tile_by_idx(column_idx, tile_idx, transaction, None)\n    }\n\n    pub fn remove_tile_by_idx(\n        &mut self,\n        column_idx: usize,\n        tile_idx: usize,\n        transaction: Transaction,\n        anim_config: Option<niri_config::Animation>,\n    ) -> RemovedTile<W> {\n        // If this is the only tile in the column, remove the whole column.\n        if self.columns[column_idx].tiles.len() == 1 {\n            let mut column = self.remove_column_by_idx(column_idx, anim_config);\n            return RemovedTile {\n                tile: column.tiles.remove(tile_idx),\n                width: column.width,\n                is_full_width: column.is_full_width,\n                is_floating: false,\n            };\n        }\n\n        let column = &mut self.columns[column_idx];\n        let prev_width = self.data[column_idx].width;\n\n        let movement_config = anim_config.unwrap_or(self.options.animations.window_movement.0);\n\n        // Animate movement of other tiles.\n        // FIXME: tiles can move by X too, in a centered or resizing layout with one window smaller\n        // than the others.\n        let offset_y = column.tile_offset(tile_idx + 1).y - column.tile_offset(tile_idx).y;\n        for tile in &mut column.tiles[tile_idx + 1..] {\n            tile.animate_move_y_from(offset_y);\n        }\n\n        if column.display_mode == ColumnDisplay::Tabbed && tile_idx != column.active_tile_idx {\n            // Fade in when removing background tab from a tabbed column.\n            let tile = &mut column.tiles[tile_idx];\n            tile.animate_alpha(0., 1., movement_config);\n        }\n\n        let was_normal = column.sizing_mode().is_normal();\n\n        let tile = column.tiles.remove(tile_idx);\n        column.data.remove(tile_idx);\n\n        // If an active column became non-fullscreen after removing the tile, clear the stored\n        // unfullscreen offset.\n        if column_idx == self.active_column_idx && !was_normal && column.sizing_mode().is_normal() {\n            self.view_offset_to_restore = None;\n        }\n\n        // If one window is left, reset its weight to 1.\n        if column.data.len() == 1 {\n            if let WindowHeight::Auto { weight } = &mut column.data[0].height {\n                *weight = 1.;\n            }\n        }\n\n        // Stop interactive resize.\n        if let Some(resize) = &self.interactive_resize {\n            if tile.window().id() == &resize.window {\n                self.interactive_resize = None;\n            }\n        }\n\n        let tile = RemovedTile {\n            tile,\n            width: column.width,\n            is_full_width: column.is_full_width,\n            is_floating: false,\n        };\n\n        #[allow(clippy::comparison_chain)] // What do you even want here?\n        if tile_idx < column.active_tile_idx {\n            // A tile above was removed; preserve the current position.\n            column.active_tile_idx -= 1;\n        } else if tile_idx == column.active_tile_idx {\n            // The active tile was removed, so the active tile index shifted to the next tile.\n            if tile_idx == column.tiles.len() {\n                // The bottom tile was removed and it was active, update active idx to remain valid.\n                column.activate_idx(tile_idx - 1);\n            } else {\n                // Ensure the newly active tile animates to opaque.\n                column.tiles[tile_idx].ensure_alpha_animates_to_1();\n            }\n        }\n\n        column.update_tile_sizes_with_transaction(true, transaction);\n        self.data[column_idx].update(column);\n        let offset = prev_width - column.width();\n\n        // Animate movement of the other columns.\n        if self.active_column_idx <= column_idx {\n            for col in &mut self.columns[column_idx + 1..] {\n                col.animate_move_from_with_config(offset, movement_config);\n            }\n        } else {\n            for col in &mut self.columns[..=column_idx] {\n                col.animate_move_from_with_config(-offset, movement_config);\n            }\n        }\n\n        tile\n    }\n\n    pub fn remove_active_column(&mut self) -> Option<Column<W>> {\n        if self.columns.is_empty() {\n            return None;\n        }\n\n        Some(self.remove_column_by_idx(self.active_column_idx, None))\n    }\n\n    pub fn remove_column_by_idx(\n        &mut self,\n        column_idx: usize,\n        anim_config: Option<niri_config::Animation>,\n    ) -> Column<W> {\n        // Animate movement of the other columns.\n        let movement_config = anim_config.unwrap_or(self.options.animations.window_movement.0);\n        let offset = self.column_x(column_idx + 1) - self.column_x(column_idx);\n        if self.active_column_idx <= column_idx {\n            for col in &mut self.columns[column_idx + 1..] {\n                col.animate_move_from_with_config(offset, movement_config);\n            }\n        } else {\n            for col in &mut self.columns[..column_idx] {\n                col.animate_move_from_with_config(-offset, movement_config);\n            }\n        }\n\n        let column = self.columns.remove(column_idx);\n        self.data.remove(column_idx);\n\n        // Stop interactive resize.\n        if let Some(resize) = &self.interactive_resize {\n            if column\n                .tiles\n                .iter()\n                .any(|tile| tile.window().id() == &resize.window)\n            {\n                self.interactive_resize = None;\n            }\n        }\n\n        if column_idx + 1 == self.active_column_idx {\n            // The previous column, that we were going to activate upon removal of the active\n            // column, has just been itself removed.\n            self.activate_prev_column_on_removal = None;\n        }\n\n        if column_idx == self.active_column_idx {\n            self.view_offset_to_restore = None;\n        }\n\n        if self.columns.is_empty() {\n            return column;\n        }\n\n        let view_config = anim_config.unwrap_or(self.options.animations.horizontal_view_movement.0);\n\n        if column_idx < self.active_column_idx {\n            // A column to the left was removed; preserve the current position.\n            // FIXME: preserve activate_prev_column_on_removal.\n            self.active_column_idx -= 1;\n            self.activate_prev_column_on_removal = None;\n        } else if column_idx == self.active_column_idx\n            && self.activate_prev_column_on_removal.is_some()\n        {\n            // The active column was removed, and we needed to activate the previous column.\n            if 0 < column_idx {\n                let prev_offset = self.activate_prev_column_on_removal.unwrap();\n\n                self.activate_column_with_anim_config(self.active_column_idx - 1, view_config);\n\n                // Restore the view offset but make sure to scroll the view in case the\n                // previous window had resized.\n                self.animate_view_offset_with_config(\n                    self.active_column_idx,\n                    prev_offset,\n                    view_config,\n                );\n                self.animate_view_offset_to_column_with_config(\n                    None,\n                    self.active_column_idx,\n                    None,\n                    view_config,\n                );\n            }\n        } else {\n            self.activate_column_with_anim_config(\n                min(self.active_column_idx, self.columns.len() - 1),\n                view_config,\n            );\n        }\n\n        column\n    }\n\n    pub fn update_window(&mut self, window: &W::Id, serial: Option<Serial>) {\n        let (col_idx, column) = self\n            .columns\n            .iter_mut()\n            .enumerate()\n            .find(|(_, col)| col.contains(window))\n            .unwrap();\n        let was_normal = column.sizing_mode().is_normal();\n        let prev_origin = column.tiles_origin();\n\n        let (tile_idx, tile) = column\n            .tiles\n            .iter_mut()\n            .enumerate()\n            .find(|(_, tile)| tile.window().id() == window)\n            .unwrap();\n\n        let resize = tile.window_mut().interactive_resize_data();\n\n        // Do this before calling update_window() so it can get up-to-date info.\n        if let Some(serial) = serial {\n            tile.window_mut().on_commit(serial);\n        }\n\n        let prev_width = self.data[col_idx].width;\n\n        column.update_window(window);\n        self.data[col_idx].update(column);\n        column.update_tile_sizes(false);\n\n        let offset = prev_width - self.data[col_idx].width;\n\n        // Move other columns in tandem with resizing.\n        let ongoing_resize_anim = column.tiles[tile_idx].resize_animation().is_some();\n        if offset != 0. {\n            if self.active_column_idx <= col_idx {\n                for col in &mut self.columns[col_idx + 1..] {\n                    // If there's a resize animation on the tile (that may have just started in\n                    // column.update_window()), then the apparent size change is smooth with no\n                    // sudden jumps. This corresponds to adding an X animation to adjacent columns.\n                    //\n                    // There could also be no resize animation with nonzero offset. This could\n                    // happen for example:\n                    // - if the window resized on its own, which we don't animate\n                    // - if the window resized by less than 10 px (the resize threshold)\n                    //\n                    // The latter case could also cancel an ongoing resize animation.\n                    //\n                    // Now, stationary columns shouldn't react to this offset change in any way,\n                    // i.e. their apparent X position should jump together with the resize.\n                    // However, adjacent columns that are already animating an X movement should\n                    // offset their animations to avoid the jump.\n                    //\n                    // Notably, this is necessary to fix the animation jump when resizing width back\n                    // and forth in quick succession (in a way that cancels the resize animation).\n                    if ongoing_resize_anim {\n                        col.animate_move_from_with_config(\n                            offset,\n                            self.options.animations.window_resize.anim,\n                        );\n                    } else {\n                        col.offset_move_anim_current(offset);\n                    }\n                }\n            } else {\n                for col in &mut self.columns[..=col_idx] {\n                    if ongoing_resize_anim {\n                        col.animate_move_from_with_config(\n                            -offset,\n                            self.options.animations.window_resize.anim,\n                        );\n                    } else {\n                        col.offset_move_anim_current(-offset);\n                    }\n                }\n            }\n        }\n\n        // When a column goes between fullscreen and non-fullscreen, the tiles origin can change.\n        // The change comes from things like ignoring struts and hiding the tab indicator in\n        // fullscreen, so both in X and Y directions.\n        let column = &mut self.columns[col_idx];\n        let new_origin = column.tiles_origin();\n        let origin_delta = prev_origin - new_origin;\n        if origin_delta != Point::new(0., 0.) {\n            for (tile, _pos) in column.tiles_mut() {\n                tile.animate_move_from(origin_delta);\n            }\n        }\n\n        if col_idx == self.active_column_idx {\n            // If offset == 0, then don't mess with the view or the gesture. Some clients (Firefox,\n            // Chromium, Electron) currently don't commit after the ack of a configure that drops\n            // the Resizing state, which can trigger this code path for a while.\n            let resize = if offset != 0. { resize } else { None };\n            if let Some(resize) = resize {\n                // Don't bother with the gesture.\n                self.view_offset.cancel_gesture();\n\n                // If this is an interactive resize commit of an active window, then we need to\n                // either preserve the view offset or adjust it accordingly.\n                let centered = self.is_centering_focused_column();\n\n                let width = self.data[col_idx].width;\n                let offset = if centered {\n                    // FIXME: when view_offset becomes fractional, this can be made additive too.\n                    let new_offset =\n                        -(self.working_area.size.w - width) / 2. - self.working_area.loc.x;\n                    new_offset - self.view_offset.target()\n                } else if resize.edges.contains(ResizeEdge::LEFT) {\n                    -offset\n                } else {\n                    0.\n                };\n\n                self.view_offset.offset(offset);\n            }\n\n            // When the active column goes fullscreen, store the view offset to restore later.\n            let is_normal = self.columns[col_idx].sizing_mode().is_normal();\n            if was_normal && !is_normal {\n                self.view_offset_to_restore = Some(self.view_offset.stationary());\n            }\n\n            // Upon unfullscreening, restore the view offset.\n            //\n            // In tabbed display mode, there can be multiple tiles in a fullscreen column. They\n            // will unfullscreen one by one, and the column width will shrink only when the\n            // last tile unfullscreens. This is when we want to restore the view offset,\n            // otherwise it will immediately reset back by the animate_view_offset below.\n            let unfullscreen_offset = if !was_normal && is_normal {\n                // Take the value unconditionally, even if the view is currently frozen by\n                // a view gesture. It shouldn't linger around because it's only valid for this\n                // particular unfullscreen.\n                self.view_offset_to_restore.take()\n            } else {\n                None\n            };\n\n            // We might need to move the view to ensure the resized window is still visible. But\n            // only do it when the view isn't frozen by an interactive resize or a view gesture.\n            if self.interactive_resize.is_none() && !self.view_offset.is_gesture() {\n                // Synchronize the horizontal view movement with the resize so that it looks nice.\n                // This is especially important for always-centered view.\n                let config = if ongoing_resize_anim {\n                    self.options.animations.window_resize.anim\n                } else {\n                    self.options.animations.horizontal_view_movement.0\n                };\n\n                // Restore the view offset upon unfullscreening if needed.\n                if let Some(prev_offset) = unfullscreen_offset {\n                    self.animate_view_offset_with_config(col_idx, prev_offset, config);\n                }\n\n                // FIXME: we will want to skip the animation in some cases here to make continuously\n                // resizing windows not look janky.\n                self.animate_view_offset_to_column_with_config(None, col_idx, None, config);\n            }\n        }\n    }\n\n    pub fn scroll_amount_to_activate(&self, window: &W::Id) -> f64 {\n        let column_idx = self\n            .columns\n            .iter()\n            .position(|col| col.contains(window))\n            .unwrap();\n\n        if self.active_column_idx == column_idx {\n            return 0.;\n        }\n\n        // Consider the end of an ongoing animation because that's what compute to fit does too.\n        let target_x = self.target_view_pos();\n        let new_view_offset = self.compute_new_view_offset_for_column(\n            Some(target_x),\n            column_idx,\n            Some(self.active_column_idx),\n        );\n\n        let new_col_x = self.column_x(column_idx);\n        let from_view_offset = target_x - new_col_x;\n\n        (from_view_offset - new_view_offset).abs() / self.working_area.size.w\n    }\n\n    pub fn activate_window(&mut self, window: &W::Id) -> bool {\n        let column_idx = self.columns.iter().position(|col| col.contains(window));\n        let Some(column_idx) = column_idx else {\n            return false;\n        };\n        let column = &mut self.columns[column_idx];\n\n        column.activate_window(window);\n        self.activate_column(column_idx);\n\n        true\n    }\n\n    pub fn start_close_animation_for_window(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        window: &W::Id,\n        blocker: TransactionBlocker,\n    ) {\n        let (tile, mut tile_pos) = self\n            .tiles_with_render_positions_mut(false)\n            .find(|(tile, _)| tile.window().id() == window)\n            .unwrap();\n\n        let Some(snapshot) = tile.take_unmap_snapshot() else {\n            return;\n        };\n\n        let tile_size = tile.tile_size();\n\n        let (col_idx, tile_idx) = self\n            .columns\n            .iter()\n            .enumerate()\n            .find_map(|(col_idx, col)| {\n                col.tiles\n                    .iter()\n                    .position(|tile| tile.window().id() == window)\n                    .map(move |tile_idx| (col_idx, tile_idx))\n            })\n            .unwrap();\n\n        let col = &self.columns[col_idx];\n        let removing_last = col.tiles.len() == 1;\n\n        // Skip closing animation for invisible tiles in a tabbed column.\n        if col.display_mode == ColumnDisplay::Tabbed && tile_idx != col.active_tile_idx {\n            return;\n        }\n\n        tile_pos.x += self.view_pos();\n\n        if col_idx < self.active_column_idx {\n            let offset = if removing_last {\n                self.column_x(col_idx + 1) - self.column_x(col_idx)\n            } else {\n                self.data[col_idx].width\n                    - col\n                        .data\n                        .iter()\n                        .enumerate()\n                        .filter_map(|(idx, data)| {\n                            (idx != tile_idx).then_some(NotNan::new(data.size.w).unwrap())\n                        })\n                        .max()\n                        .map(NotNan::into_inner)\n                        .unwrap()\n            };\n            tile_pos.x -= offset;\n        }\n\n        self.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);\n    }\n\n    fn start_close_animation_for_tile(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        snapshot: TileRenderSnapshot,\n        tile_size: Size<f64, Logical>,\n        tile_pos: Point<f64, Logical>,\n        blocker: TransactionBlocker,\n    ) {\n        let anim = Animation::new(\n            self.clock.clone(),\n            0.,\n            1.,\n            0.,\n            self.options.animations.window_close.anim,\n        );\n\n        let blocker = if self.options.disable_transactions {\n            TransactionBlocker::completed()\n        } else {\n            blocker\n        };\n\n        let scale = Scale::from(self.scale);\n        let res = ClosingWindow::new(\n            renderer, snapshot, scale, tile_size, tile_pos, blocker, anim,\n        );\n        match res {\n            Ok(closing) => {\n                self.closing_windows.push(closing);\n            }\n            Err(err) => {\n                warn!(\"error creating a closing window animation: {err:?}\");\n            }\n        }\n    }\n\n    pub fn start_open_animation(&mut self, id: &W::Id) -> bool {\n        self.columns\n            .iter_mut()\n            .any(|col| col.start_open_animation(id))\n    }\n\n    pub fn focus_left(&mut self) -> bool {\n        if self.active_column_idx == 0 {\n            return false;\n        }\n        self.activate_column(self.active_column_idx - 1);\n        true\n    }\n\n    pub fn focus_right(&mut self) -> bool {\n        if self.active_column_idx + 1 >= self.columns.len() {\n            return false;\n        }\n\n        self.activate_column(self.active_column_idx + 1);\n        true\n    }\n\n    pub fn focus_column_first(&mut self) {\n        self.activate_column(0);\n    }\n\n    pub fn focus_column_last(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        self.activate_column(self.columns.len() - 1);\n    }\n\n    pub fn focus_column(&mut self, index: usize) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        self.activate_column(index.saturating_sub(1).min(self.columns.len() - 1));\n    }\n\n    pub fn focus_window_in_column(&mut self, index: u8) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        self.columns[self.active_column_idx].focus_index(index);\n    }\n\n    pub fn focus_down(&mut self) -> bool {\n        if self.columns.is_empty() {\n            return false;\n        }\n\n        self.columns[self.active_column_idx].focus_down()\n    }\n\n    pub fn focus_up(&mut self) -> bool {\n        if self.columns.is_empty() {\n            return false;\n        }\n\n        self.columns[self.active_column_idx].focus_up()\n    }\n\n    pub fn focus_down_or_left(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let column = &mut self.columns[self.active_column_idx];\n        if !column.focus_down() {\n            self.focus_left();\n        }\n    }\n\n    pub fn focus_down_or_right(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let column = &mut self.columns[self.active_column_idx];\n        if !column.focus_down() {\n            self.focus_right();\n        }\n    }\n\n    pub fn focus_up_or_left(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let column = &mut self.columns[self.active_column_idx];\n        if !column.focus_up() {\n            self.focus_left();\n        }\n    }\n\n    pub fn focus_up_or_right(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let column = &mut self.columns[self.active_column_idx];\n        if !column.focus_up() {\n            self.focus_right();\n        }\n    }\n\n    pub fn focus_top(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        self.columns[self.active_column_idx].focus_top()\n    }\n\n    pub fn focus_bottom(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        self.columns[self.active_column_idx].focus_bottom()\n    }\n\n    pub fn move_column_to_index(&mut self, index: usize) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        self.move_column_to(index.saturating_sub(1).min(self.columns.len() - 1));\n    }\n\n    fn move_column_to(&mut self, new_idx: usize) {\n        if self.active_column_idx == new_idx {\n            return;\n        }\n\n        let current_col_x = self.column_x(self.active_column_idx);\n        let next_col_x = self.column_x(self.active_column_idx + 1);\n\n        let mut column = self.columns.remove(self.active_column_idx);\n        let data = self.data.remove(self.active_column_idx);\n        cancel_resize_for_column(&mut self.interactive_resize, &mut column);\n        self.columns.insert(new_idx, column);\n        self.data.insert(new_idx, data);\n\n        // Preserve the camera position when moving to the left.\n        let view_offset_delta = -self.column_x(self.active_column_idx) + current_col_x;\n        self.view_offset.offset(view_offset_delta);\n\n        // The column we just moved is offset by the difference between its new and old position.\n        let new_col_x = self.column_x(new_idx);\n        self.columns[new_idx].animate_move_from(current_col_x - new_col_x);\n\n        // All columns in between moved by the width of the column that we just moved.\n        let others_x_offset = next_col_x - current_col_x;\n        if self.active_column_idx < new_idx {\n            for col in &mut self.columns[self.active_column_idx..new_idx] {\n                col.animate_move_from(others_x_offset);\n            }\n        } else {\n            for col in &mut self.columns[new_idx + 1..=self.active_column_idx] {\n                col.animate_move_from(-others_x_offset);\n            }\n        }\n\n        self.activate_column_with_anim_config(new_idx, self.options.animations.window_movement.0);\n    }\n\n    pub fn move_left(&mut self) -> bool {\n        if self.active_column_idx == 0 {\n            return false;\n        }\n\n        self.move_column_to(self.active_column_idx - 1);\n        true\n    }\n\n    pub fn move_right(&mut self) -> bool {\n        let new_idx = self.active_column_idx + 1;\n        if new_idx >= self.columns.len() {\n            return false;\n        }\n\n        self.move_column_to(new_idx);\n        true\n    }\n\n    pub fn move_column_to_first(&mut self) {\n        self.move_column_to(0);\n    }\n\n    pub fn move_column_to_last(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let new_idx = self.columns.len() - 1;\n        self.move_column_to(new_idx);\n    }\n\n    pub fn move_down(&mut self) -> bool {\n        if self.columns.is_empty() {\n            return false;\n        }\n\n        self.columns[self.active_column_idx].move_down()\n    }\n\n    pub fn move_up(&mut self) -> bool {\n        if self.columns.is_empty() {\n            return false;\n        }\n\n        self.columns[self.active_column_idx].move_up()\n    }\n\n    pub fn consume_or_expel_window_left(&mut self, window: Option<&W::Id>) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let (source_col_idx, source_tile_idx) = if let Some(window) = window {\n            self.columns\n                .iter_mut()\n                .enumerate()\n                .find_map(|(col_idx, col)| {\n                    col.tiles\n                        .iter()\n                        .position(|tile| tile.window().id() == window)\n                        .map(|tile_idx| (col_idx, tile_idx))\n                })\n                .unwrap()\n        } else {\n            let source_col_idx = self.active_column_idx;\n            let source_tile_idx = self.columns[self.active_column_idx].active_tile_idx;\n            (source_col_idx, source_tile_idx)\n        };\n\n        let source_column = &self.columns[source_col_idx];\n        let prev_off = source_column.tile_offset(source_tile_idx);\n\n        let source_tile_was_active = self.active_column_idx == source_col_idx\n            && source_column.active_tile_idx == source_tile_idx;\n\n        if source_column.tiles.len() == 1 {\n            if source_col_idx == 0 {\n                return;\n            }\n\n            // Move into adjacent column.\n            let target_column_idx = source_col_idx - 1;\n\n            let offset = if self.active_column_idx <= source_col_idx {\n                // Tiles to the right animate from the following column.\n                self.column_x(source_col_idx) - self.column_x(target_column_idx)\n            } else {\n                // Tiles to the left animate to preserve their right edge position.\n                f64::max(\n                    0.,\n                    self.data[target_column_idx].width - self.data[source_col_idx].width,\n                )\n            };\n            let mut offset = Point::from((offset, 0.));\n\n            if source_tile_was_active {\n                // Make sure the previous (target) column is activated so the animation looks right.\n                //\n                // However, if it was already going to be activated, leave the offset as is. This\n                // improves the workflow that has become common with tabbed columns: open a new\n                // window, then immediately consume it left as a new tab.\n                self.activate_prev_column_on_removal\n                    .get_or_insert(self.view_offset.stationary() + offset.x);\n            }\n\n            offset.x += self.columns[source_col_idx].render_offset().x;\n            let RemovedTile { tile, .. } = self.remove_tile_by_idx(\n                source_col_idx,\n                0,\n                Transaction::new(),\n                Some(self.options.animations.window_movement.0),\n            );\n            self.add_tile_to_column(target_column_idx, None, tile, source_tile_was_active);\n\n            let target_column = &mut self.columns[target_column_idx];\n            offset.x -= target_column.render_offset().x;\n            offset += prev_off - target_column.tile_offset(target_column.tiles.len() - 1);\n\n            let new_tile = target_column.tiles.last_mut().unwrap();\n            new_tile.animate_move_from(offset);\n        } else {\n            // Move out of column.\n            let mut offset = Point::from((source_column.render_offset().x, 0.));\n\n            let removed =\n                self.remove_tile_by_idx(source_col_idx, source_tile_idx, Transaction::new(), None);\n\n            // We're inserting into the source column position.\n            let target_column_idx = source_col_idx;\n\n            self.add_tile(\n                Some(target_column_idx),\n                removed.tile,\n                source_tile_was_active,\n                removed.width,\n                removed.is_full_width,\n                Some(self.options.animations.window_movement.0),\n            );\n\n            if source_tile_was_active {\n                // We added to the left, don't activate even further left on removal.\n                self.activate_prev_column_on_removal = None;\n            }\n\n            if target_column_idx <= self.active_column_idx {\n                // Tiles to the left animate from the following column.\n                offset.x += self.column_x(target_column_idx + 1) - self.column_x(target_column_idx);\n            }\n\n            let new_col = &mut self.columns[target_column_idx];\n            offset += prev_off - new_col.tile_offset(0);\n            new_col.tiles[0].animate_move_from(offset);\n        }\n    }\n\n    pub fn consume_or_expel_window_right(&mut self, window: Option<&W::Id>) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let (source_col_idx, source_tile_idx) = if let Some(window) = window {\n            self.columns\n                .iter_mut()\n                .enumerate()\n                .find_map(|(col_idx, col)| {\n                    col.tiles\n                        .iter()\n                        .position(|tile| tile.window().id() == window)\n                        .map(|tile_idx| (col_idx, tile_idx))\n                })\n                .unwrap()\n        } else {\n            let source_col_idx = self.active_column_idx;\n            let source_tile_idx = self.columns[self.active_column_idx].active_tile_idx;\n            (source_col_idx, source_tile_idx)\n        };\n\n        let cur_x = self.column_x(source_col_idx);\n\n        let source_column = &self.columns[source_col_idx];\n        let mut offset = Point::from((source_column.render_offset().x, 0.));\n        let prev_off = source_column.tile_offset(source_tile_idx);\n\n        let source_tile_was_active = self.active_column_idx == source_col_idx\n            && source_column.active_tile_idx == source_tile_idx;\n\n        if source_column.tiles.len() == 1 {\n            if source_col_idx + 1 == self.columns.len() {\n                return;\n            }\n\n            // Move into adjacent column.\n            let target_column_idx = source_col_idx;\n\n            offset.x += cur_x - self.column_x(source_col_idx + 1);\n            offset.x -= self.columns[source_col_idx + 1].render_offset().x;\n\n            if source_tile_was_active {\n                // Make sure the target column gets activated.\n                self.activate_prev_column_on_removal = None;\n            }\n\n            let RemovedTile { tile, .. } = self.remove_tile_by_idx(\n                source_col_idx,\n                0,\n                Transaction::new(),\n                Some(self.options.animations.window_movement.0),\n            );\n            self.add_tile_to_column(target_column_idx, None, tile, source_tile_was_active);\n\n            let target_column = &mut self.columns[target_column_idx];\n            offset += prev_off - target_column.tile_offset(target_column.tiles.len() - 1);\n\n            let new_tile = target_column.tiles.last_mut().unwrap();\n            new_tile.animate_move_from(offset);\n        } else {\n            // Move out of column.\n            let prev_width = self.data[source_col_idx].width;\n\n            let removed =\n                self.remove_tile_by_idx(source_col_idx, source_tile_idx, Transaction::new(), None);\n\n            let target_column_idx = source_col_idx + 1;\n\n            self.add_tile(\n                Some(target_column_idx),\n                removed.tile,\n                source_tile_was_active,\n                removed.width,\n                removed.is_full_width,\n                Some(self.options.animations.window_movement.0),\n            );\n\n            offset.x += if self.active_column_idx <= target_column_idx {\n                // Tiles to the right animate to the following column.\n                cur_x - self.column_x(target_column_idx)\n            } else {\n                // Tiles to the left animate for a change in width.\n                -f64::max(0., prev_width - self.data[target_column_idx].width)\n            };\n\n            let new_col = &mut self.columns[target_column_idx];\n            offset += prev_off - new_col.tile_offset(0);\n            new_col.tiles[0].animate_move_from(offset);\n        }\n    }\n\n    pub fn consume_into_column(&mut self) {\n        if self.columns.len() < 2 {\n            return;\n        }\n\n        if self.active_column_idx == self.columns.len() - 1 {\n            return;\n        }\n\n        let target_column_idx = self.active_column_idx;\n        let source_column_idx = self.active_column_idx + 1;\n\n        let offset = self.column_x(source_column_idx)\n            + self.columns[source_column_idx].render_offset().x\n            - self.column_x(target_column_idx);\n        let mut offset = Point::from((offset, 0.));\n        let prev_off = self.columns[source_column_idx].tile_offset(0);\n\n        let removed = self.remove_tile_by_idx(source_column_idx, 0, Transaction::new(), None);\n        self.add_tile_to_column(target_column_idx, None, removed.tile, false);\n\n        let target_column = &mut self.columns[target_column_idx];\n        offset += prev_off - target_column.tile_offset(target_column.tiles.len() - 1);\n        offset.x -= target_column.render_offset().x;\n\n        let new_tile = target_column.tiles.last_mut().unwrap();\n        new_tile.animate_move_from(offset);\n    }\n\n    pub fn expel_from_column(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let source_col_idx = self.active_column_idx;\n        let target_col_idx = self.active_column_idx + 1;\n        let cur_x = self.column_x(source_col_idx);\n\n        let source_column = &self.columns[self.active_column_idx];\n        if source_column.tiles.len() == 1 {\n            return;\n        }\n\n        let source_tile_idx = source_column.tiles.len() - 1;\n\n        let mut offset = Point::from((source_column.render_offset().x, 0.));\n        let prev_off = source_column.tile_offset(source_tile_idx);\n\n        let removed =\n            self.remove_tile_by_idx(source_col_idx, source_tile_idx, Transaction::new(), None);\n\n        self.add_tile(\n            Some(target_col_idx),\n            removed.tile,\n            false,\n            removed.width,\n            removed.is_full_width,\n            Some(self.options.animations.window_movement.0),\n        );\n\n        offset.x += cur_x - self.column_x(target_col_idx);\n\n        let new_col = &mut self.columns[target_col_idx];\n        offset += prev_off - new_col.tile_offset(0);\n        new_col.tiles[0].animate_move_from(offset);\n    }\n\n    pub fn swap_window_in_direction(&mut self, direction: ScrollDirection) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        // if this is the first (resp. last column), then this operation is equivalent\n        // to an `consume_or_expel_window_left` (resp. `consume_or_expel_window_right`)\n        match direction {\n            ScrollDirection::Left => {\n                if self.active_column_idx == 0 {\n                    return;\n                }\n            }\n            ScrollDirection::Right => {\n                if self.active_column_idx == self.columns.len() - 1 {\n                    return;\n                }\n            }\n        }\n\n        let source_column_idx = self.active_column_idx;\n        let target_column_idx = self.active_column_idx.wrapping_add_signed(match direction {\n            ScrollDirection::Left => -1,\n            ScrollDirection::Right => 1,\n        });\n\n        // if both source and target columns contain a single tile, then the operation is equivalent\n        // to a simple column move\n        if self.columns[source_column_idx].tiles.len() == 1\n            && self.columns[target_column_idx].tiles.len() == 1\n        {\n            return self.move_column_to(target_column_idx);\n        }\n\n        let source_tile_idx = self.columns[source_column_idx].active_tile_idx;\n        let target_tile_idx = self.columns[target_column_idx].active_tile_idx;\n        let source_column_drained = self.columns[source_column_idx].tiles.len() == 1;\n\n        // capture the original positions of the tiles\n        let (mut source_pt, mut target_pt) = (\n            self.columns[source_column_idx].render_offset()\n                + self.columns[source_column_idx].tile_offset(source_tile_idx),\n            self.columns[target_column_idx].render_offset()\n                + self.columns[target_column_idx].tile_offset(target_tile_idx),\n        );\n        source_pt.x += self.column_x(source_column_idx);\n        target_pt.x += self.column_x(target_column_idx);\n\n        let transaction = Transaction::new();\n\n        // If the source column contains a single tile, this will also remove the column.\n        // When this happens `source_column_drained` will be set and the column will need to be\n        // recreated with `add_tile`\n        let source_removed = self.remove_tile_by_idx(\n            source_column_idx,\n            source_tile_idx,\n            transaction.clone(),\n            None,\n        );\n\n        {\n            // special case when the source column disappears after removing its last tile\n            let adjusted_target_column_idx =\n                if direction == ScrollDirection::Right && source_column_drained {\n                    target_column_idx - 1\n                } else {\n                    target_column_idx\n                };\n\n            self.add_tile_to_column(\n                adjusted_target_column_idx,\n                Some(target_tile_idx),\n                source_removed.tile,\n                false,\n            );\n\n            let RemovedTile {\n                tile: target_tile, ..\n            } = self.remove_tile_by_idx(\n                adjusted_target_column_idx,\n                target_tile_idx + 1,\n                transaction.clone(),\n                None,\n            );\n\n            if source_column_drained {\n                // recreate the drained column with only the target tile\n                self.add_tile(\n                    Some(source_column_idx),\n                    target_tile,\n                    true,\n                    source_removed.width,\n                    source_removed.is_full_width,\n                    None,\n                )\n            } else {\n                // simply add the removed target tile to the source column\n                self.add_tile_to_column(\n                    source_column_idx,\n                    Some(source_tile_idx),\n                    target_tile,\n                    false,\n                );\n            }\n        }\n\n        // update the active tile in the modified columns\n        self.columns[source_column_idx].active_tile_idx = source_tile_idx;\n        self.columns[target_column_idx].active_tile_idx = target_tile_idx;\n\n        // Animations\n        self.columns[target_column_idx].tiles[target_tile_idx]\n            .animate_move_from(source_pt - target_pt);\n        self.columns[target_column_idx].tiles[target_tile_idx].ensure_alpha_animates_to_1();\n\n        // FIXME: this stop_move_animations() causes the target tile animation to \"reset\" when\n        // swapping. It's here as a workaround to stop the unwanted animation of moving the source\n        // tile down when adding the target tile above it. This code needs to be written in some\n        // other way not to trigger that animation, or to cancel it properly, so that swap doesn't\n        // cancel all ongoing target tile animations.\n        self.columns[source_column_idx].tiles[source_tile_idx].stop_move_animations();\n        self.columns[source_column_idx].tiles[source_tile_idx]\n            .animate_move_from(target_pt - source_pt);\n        self.columns[source_column_idx].tiles[source_tile_idx].ensure_alpha_animates_to_1();\n\n        self.activate_column(target_column_idx);\n    }\n\n    pub fn toggle_column_tabbed_display(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        let display = match col.display_mode {\n            ColumnDisplay::Normal => ColumnDisplay::Tabbed,\n            ColumnDisplay::Tabbed => ColumnDisplay::Normal,\n        };\n\n        self.set_column_display(display);\n    }\n\n    pub fn set_column_display(&mut self, display: ColumnDisplay) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        if col.display_mode == display {\n            return;\n        }\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n        col.set_column_display(display);\n\n        // With place_within_column, the tab indicator changes the column size immediately.\n        self.data[self.active_column_idx].update(col);\n        col.update_tile_sizes(true);\n\n        // Disable fullscreen if needed.\n        if col.display_mode != ColumnDisplay::Tabbed && col.tiles.len() > 1 {\n            let window = col.tiles[col.active_tile_idx].window().id().clone();\n            self.set_fullscreen(&window, false);\n            self.set_maximized(&window, false);\n        }\n    }\n\n    pub fn center_column(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        self.animate_view_offset_to_column_centered(\n            None,\n            self.active_column_idx,\n            self.options.animations.horizontal_view_movement.0,\n        );\n\n        let col = &mut self.columns[self.active_column_idx];\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn center_window(&mut self, window: Option<&W::Id>) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let col_idx = if let Some(window) = window {\n            self.columns\n                .iter()\n                .position(|col| col.contains(window))\n                .unwrap()\n        } else {\n            self.active_column_idx\n        };\n\n        // We can reasonably center only the active column.\n        if col_idx != self.active_column_idx {\n            return;\n        }\n\n        self.center_column();\n    }\n\n    pub fn center_visible_columns(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        if self.is_centering_focused_column() {\n            return;\n        }\n\n        // Consider the end of an ongoing animation because that's what compute to fit does too.\n        let view_x = self.target_view_pos();\n        let working_x = self.working_area.loc.x;\n        let working_w = self.working_area.size.w;\n\n        // Count all columns that are fully visible inside the working area.\n        let mut width_taken = 0.;\n        let mut leftmost_col_x = None;\n        let mut active_col_x = None;\n\n        let gap = self.options.layout.gaps;\n        let col_xs = self.column_xs(self.data.iter().copied());\n        for (idx, col_x) in col_xs.take(self.columns.len()).enumerate() {\n            if col_x < view_x + working_x + gap {\n                // Column goes off-screen to the left.\n                continue;\n            }\n\n            leftmost_col_x.get_or_insert(col_x);\n\n            let width = self.data[idx].width;\n            if view_x + working_x + working_w < col_x + width + gap {\n                // Column goes off-screen to the right. We can stop here.\n                break;\n            }\n\n            if idx == self.active_column_idx {\n                active_col_x = Some(col_x);\n            }\n\n            width_taken += width + gap;\n        }\n\n        if active_col_x.is_none() {\n            // The active column wasn't fully on screen, so we can't meaningfully do anything.\n            return;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n\n        let free_space = working_w - width_taken + gap;\n        let new_view_x = leftmost_col_x.unwrap() - free_space / 2. - working_x;\n\n        self.animate_view_offset(self.active_column_idx, new_view_x - active_col_x.unwrap());\n        // Just in case.\n        self.animate_view_offset_to_column(None, self.active_column_idx, None);\n    }\n\n    pub fn view_pos(&self) -> f64 {\n        self.column_x(self.active_column_idx) + self.view_offset.current()\n    }\n\n    pub fn target_view_pos(&self) -> f64 {\n        self.column_x(self.active_column_idx) + self.view_offset.target()\n    }\n\n    // HACK: pass a self.data iterator in manually as a workaround for the lack of method partial\n    // borrowing. Note that this method's return value does not borrow the entire &Self!\n    fn column_xs(&self, data: impl Iterator<Item = ColumnData>) -> impl Iterator<Item = f64> {\n        let gaps = self.options.layout.gaps;\n        let mut x = 0.;\n\n        // Chain with a dummy value to be able to get one past all columns' X.\n        let dummy = ColumnData { width: 0. };\n        let data = data.chain(iter::once(dummy));\n\n        data.map(move |data| {\n            let rv = x;\n            x += data.width + gaps;\n            rv\n        })\n    }\n\n    fn column_x(&self, column_idx: usize) -> f64 {\n        self.column_xs(self.data.iter().copied())\n            .nth(column_idx)\n            .unwrap()\n    }\n\n    fn column_xs_in_render_order(\n        &self,\n        data: impl Iterator<Item = ColumnData>,\n    ) -> impl Iterator<Item = f64> {\n        let active_idx = self.active_column_idx;\n        let active_pos = self.column_x(active_idx);\n        let offsets = self\n            .column_xs(data)\n            .enumerate()\n            .filter_map(move |(idx, pos)| (idx != active_idx).then_some(pos));\n        iter::once(active_pos).chain(offsets)\n    }\n\n    pub fn columns(&self) -> impl Iterator<Item = &Column<W>> {\n        self.columns.iter()\n    }\n\n    fn columns_mut(&mut self) -> impl Iterator<Item = (&mut Column<W>, f64)> + '_ {\n        let offsets = self.column_xs(self.data.iter().copied());\n        zip(&mut self.columns, offsets)\n    }\n\n    fn columns_in_render_order(&self) -> impl Iterator<Item = (&Column<W>, f64)> + '_ {\n        let offsets = self.column_xs_in_render_order(self.data.iter().copied());\n\n        let (first, active, rest) = if self.columns.is_empty() {\n            (&[][..], &[][..], &[][..])\n        } else {\n            let (first, rest) = self.columns.split_at(self.active_column_idx);\n            let (active, rest) = rest.split_at(1);\n            (first, active, rest)\n        };\n\n        let columns = active.iter().chain(first).chain(rest);\n        zip(columns, offsets)\n    }\n\n    fn columns_in_render_order_mut(&mut self) -> impl Iterator<Item = (&mut Column<W>, f64)> + '_ {\n        let offsets = self.column_xs_in_render_order(self.data.iter().copied());\n\n        let (first, active, rest) = if self.columns.is_empty() {\n            (&mut [][..], &mut [][..], &mut [][..])\n        } else {\n            let (first, rest) = self.columns.split_at_mut(self.active_column_idx);\n            let (active, rest) = rest.split_at_mut(1);\n            (first, active, rest)\n        };\n\n        let columns = active.iter_mut().chain(first).chain(rest);\n        zip(columns, offsets)\n    }\n\n    pub fn tiles_with_render_positions(\n        &self,\n    ) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>, bool)> {\n        let scale = self.scale;\n        let view_off = Point::from((-self.view_pos(), 0.));\n        self.columns_in_render_order()\n            .flat_map(move |(col, col_x)| {\n                let col_off = Point::from((col_x, 0.));\n                let col_render_off = col.render_offset();\n                col.tiles_in_render_order()\n                    .map(move |(tile, tile_off, visible)| {\n                        let pos =\n                            view_off + col_off + col_render_off + tile_off + tile.render_offset();\n                        // Round to physical pixels.\n                        let pos = pos.to_physical_precise_round(scale).to_logical(scale);\n                        (tile, pos, visible)\n                    })\n            })\n    }\n\n    pub fn tiles_with_render_positions_mut(\n        &mut self,\n        round: bool,\n    ) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> {\n        let scale = self.scale;\n        let view_off = Point::from((-self.view_pos(), 0.));\n        self.columns_in_render_order_mut()\n            .flat_map(move |(col, col_x)| {\n                let col_off = Point::from((col_x, 0.));\n                let col_render_off = col.render_offset();\n                col.tiles_in_render_order_mut()\n                    .map(move |(tile, tile_off)| {\n                        let mut pos =\n                            view_off + col_off + col_render_off + tile_off + tile.render_offset();\n                        // Round to physical pixels.\n                        if round {\n                            pos = pos.to_physical_precise_round(scale).to_logical(scale);\n                        }\n                        (tile, pos)\n                    })\n            })\n    }\n\n    pub fn tiles_with_ipc_layouts(&self) -> impl Iterator<Item = (&Tile<W>, WindowLayout)> {\n        self.columns\n            .iter()\n            .enumerate()\n            .flat_map(move |(col_idx, col)| {\n                col.tiles().enumerate().map(move |(tile_idx, (tile, _))| {\n                    let layout = WindowLayout {\n                        // Our indices are 1-based, consistent with the actions.\n                        pos_in_scrolling_layout: Some((col_idx + 1, tile_idx + 1)),\n                        ..tile.ipc_layout_template()\n                    };\n                    (tile, layout)\n                })\n            })\n    }\n\n    pub(super) fn insert_hint_area(\n        &self,\n        position: InsertPosition,\n    ) -> Option<Rectangle<f64, Logical>> {\n        let mut hint_area = match position {\n            InsertPosition::NewColumn(column_index) => {\n                if column_index == 0 || column_index == self.columns.len() {\n                    let size = Size::from((\n                        300.,\n                        self.working_area.size.h - self.options.layout.gaps * 2.,\n                    ));\n                    let mut loc = Point::from((\n                        self.column_x(column_index),\n                        self.working_area.loc.y + self.options.layout.gaps,\n                    ));\n                    if column_index == 0 && !self.columns.is_empty() {\n                        loc.x -= size.w + self.options.layout.gaps;\n                    }\n                    Rectangle::new(loc, size)\n                } else if column_index > self.columns.len() {\n                    error!(\"insert hint column index is out of range\");\n                    return None;\n                } else {\n                    let size = Size::from((\n                        300.,\n                        self.working_area.size.h - self.options.layout.gaps * 2.,\n                    ));\n                    let loc = Point::from((\n                        self.column_x(column_index) - size.w / 2. - self.options.layout.gaps / 2.,\n                        self.working_area.loc.y + self.options.layout.gaps,\n                    ));\n                    Rectangle::new(loc, size)\n                }\n            }\n            InsertPosition::InColumn(column_index, tile_index) => {\n                if column_index > self.columns.len() {\n                    error!(\"insert hint column index is out of range\");\n                    return None;\n                }\n\n                let col = &self.columns[column_index];\n                if tile_index > col.tiles.len() {\n                    error!(\"insert hint tile index is out of range\");\n                    return None;\n                }\n\n                let is_tabbed = col.display_mode == ColumnDisplay::Tabbed;\n\n                let (height, y) = if is_tabbed {\n                    // In tabbed mode, there's only one tile visible, and we want to draw the hint\n                    // at its top or bottom.\n                    let top = col.tile_offset(col.active_tile_idx).y;\n                    let bottom = top + col.data[col.active_tile_idx].size.h;\n\n                    if tile_index <= col.active_tile_idx {\n                        (150., top)\n                    } else {\n                        (150., bottom - 150.)\n                    }\n                } else {\n                    let top = col.tile_offset(tile_index).y;\n\n                    if tile_index == 0 {\n                        (150., top)\n                    } else if tile_index == col.tiles.len() {\n                        (150., top - self.options.layout.gaps - 150.)\n                    } else {\n                        (300., top - self.options.layout.gaps / 2. - 150.)\n                    }\n                };\n\n                // Adjust for place-within-column tab indicator.\n                let origin_x = col.tiles_origin().x;\n                let extra_w = if is_tabbed && col.sizing_mode().is_normal() {\n                    col.tab_indicator.extra_size(col.tiles.len(), col.scale).w\n                } else {\n                    0.\n                };\n\n                let size = Size::from((self.data[column_index].width - extra_w, height));\n                let loc = Point::from((self.column_x(column_index) + origin_x, y));\n                Rectangle::new(loc, size)\n            }\n            InsertPosition::Floating => return None,\n        };\n\n        // First window on an empty workspace will cancel out any view offset. Replicate this\n        // effect here.\n        if self.columns.is_empty() {\n            let view_offset = if self.is_centering_focused_column() {\n                self.compute_new_view_offset_centered(\n                    Some(0.),\n                    0.,\n                    hint_area.size.w,\n                    SizingMode::Normal,\n                )\n            } else {\n                self.compute_new_view_offset_fit(Some(0.), 0., hint_area.size.w, SizingMode::Normal)\n            };\n            hint_area.loc.x -= view_offset;\n        } else {\n            hint_area.loc.x -= self.view_pos();\n        }\n\n        Some(hint_area)\n    }\n\n    /// Returns the geometry of the active tile relative to and clamped to the view.\n    ///\n    /// During animations, assumes the final view position.\n    pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {\n        let col = self.columns.get(self.active_column_idx)?;\n\n        let final_view_offset = self.view_offset.target();\n        let view_off = Point::from((-final_view_offset, 0.));\n\n        let (tile, tile_off) = col.tiles().nth(col.active_tile_idx).unwrap();\n\n        let tile_pos = view_off + tile_off;\n        let tile_size = tile.tile_size();\n        let tile_rect = Rectangle::new(tile_pos, tile_size);\n\n        let view = Rectangle::from_size(self.view_size);\n        view.intersection(tile_rect)\n    }\n\n    pub fn popup_target_rect(&self, id: &W::Id) -> Option<Rectangle<f64, Logical>> {\n        for col in &self.columns {\n            for (tile, pos) in col.tiles() {\n                if tile.window().id() == id {\n                    // In the scrolling layout, we try to position popups horizontally within the\n                    // window geometry (so they remain visible even if the window scrolls flush with\n                    // the left/right edge of the screen), and vertically within the whole parent\n                    // working area.\n                    let width = tile.window_size().w;\n                    let height = self.parent_area.size.h;\n\n                    let mut target = Rectangle::from_size(Size::from((width, height)));\n                    target.loc.y += self.parent_area.loc.y;\n                    target.loc.y -= pos.y;\n                    target.loc.y -= tile.window_loc().y;\n\n                    return Some(target);\n                }\n            }\n        }\n        None\n    }\n\n    pub fn toggle_width(&mut self, forwards: bool) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        col.toggle_width(None, forwards);\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn toggle_full_width(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        col.toggle_full_width();\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn set_window_width(&mut self, window: Option<&W::Id>, change: SizeChange) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let (col, tile_idx) = if let Some(window) = window {\n            self.columns\n                .iter_mut()\n                .find_map(|col| {\n                    col.tiles\n                        .iter()\n                        .position(|tile| tile.window().id() == window)\n                        .map(|tile_idx| (col, Some(tile_idx)))\n                })\n                .unwrap()\n        } else {\n            (&mut self.columns[self.active_column_idx], None)\n        };\n\n        col.set_column_width(change, tile_idx, true);\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn set_window_height(&mut self, window: Option<&W::Id>, change: SizeChange) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let (col, tile_idx) = if let Some(window) = window {\n            self.columns\n                .iter_mut()\n                .find_map(|col| {\n                    col.tiles\n                        .iter()\n                        .position(|tile| tile.window().id() == window)\n                        .map(|tile_idx| (col, Some(tile_idx)))\n                })\n                .unwrap()\n        } else {\n            (&mut self.columns[self.active_column_idx], None)\n        };\n\n        col.set_window_height(change, tile_idx, true);\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn reset_window_height(&mut self, window: Option<&W::Id>) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let (col, tile_idx) = if let Some(window) = window {\n            self.columns\n                .iter_mut()\n                .find_map(|col| {\n                    col.tiles\n                        .iter()\n                        .position(|tile| tile.window().id() == window)\n                        .map(|tile_idx| (col, Some(tile_idx)))\n                })\n                .unwrap()\n        } else {\n            (&mut self.columns[self.active_column_idx], None)\n        };\n\n        col.reset_window_height(tile_idx);\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn toggle_window_width(&mut self, window: Option<&W::Id>, forwards: bool) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let (col, tile_idx) = if let Some(window) = window {\n            self.columns\n                .iter_mut()\n                .find_map(|col| {\n                    col.tiles\n                        .iter()\n                        .position(|tile| tile.window().id() == window)\n                        .map(|tile_idx| (col, Some(tile_idx)))\n                })\n                .unwrap()\n        } else {\n            (&mut self.columns[self.active_column_idx], None)\n        };\n\n        col.toggle_width(tile_idx, forwards);\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn toggle_window_height(&mut self, window: Option<&W::Id>, forwards: bool) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let (col, tile_idx) = if let Some(window) = window {\n            self.columns\n                .iter_mut()\n                .find_map(|col| {\n                    col.tiles\n                        .iter()\n                        .position(|tile| tile.window().id() == window)\n                        .map(|tile_idx| (col, Some(tile_idx)))\n                })\n                .unwrap()\n        } else {\n            (&mut self.columns[self.active_column_idx], None)\n        };\n\n        col.toggle_window_height(tile_idx, forwards);\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n    }\n\n    pub fn expand_column_to_available_width(&mut self) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n        if !col.pending_sizing_mode().is_normal() || col.is_full_width {\n            return;\n        }\n\n        if self.is_centering_focused_column() {\n            // Always-centered mode is different since the active window position cannot be\n            // controlled (it's always at the center). I guess you could come up with different\n            // logic here that computes the width in such a way so as to leave nearby columns fully\n            // on screen while taking into account that the active column will remain centered\n            // after resizing. But I'm not sure it's that useful? So let's do the simple thing.\n            let col = &mut self.columns[self.active_column_idx];\n            col.toggle_full_width();\n            cancel_resize_for_column(&mut self.interactive_resize, col);\n            return;\n        }\n\n        // NOTE: This logic won't work entirely correctly with small fixed-size maximized windows\n        // (they have a different area and padding).\n\n        // Consider the end of an ongoing animation because that's what compute to fit does too.\n        let view_x = self.target_view_pos();\n        let working_x = self.working_area.loc.x;\n        let working_w = self.working_area.size.w;\n\n        // Count all columns that are fully visible inside the working area.\n        let mut width_taken = 0.;\n        let mut leftmost_col_x = None;\n        let mut active_col_x = None;\n        let mut counted_non_active_column = false;\n\n        let gap = self.options.layout.gaps;\n        let col_xs = self.column_xs(self.data.iter().copied());\n        for (idx, col_x) in col_xs.take(self.columns.len()).enumerate() {\n            if col_x < view_x + working_x + gap {\n                // Column goes off-screen to the left.\n                continue;\n            }\n\n            leftmost_col_x.get_or_insert(col_x);\n\n            let width = self.data[idx].width;\n            if view_x + working_x + working_w < col_x + width + gap {\n                // Column goes off-screen to the right. We can stop here.\n                break;\n            }\n\n            if idx == self.active_column_idx {\n                active_col_x = Some(col_x);\n            } else {\n                counted_non_active_column = true;\n            }\n\n            width_taken += width + gap;\n        }\n\n        if active_col_x.is_none() {\n            // The active column wasn't fully on screen, so we can't meaningfully do anything.\n            return;\n        }\n\n        let col = &mut self.columns[self.active_column_idx];\n\n        let available_width = working_w - gap - width_taken - col.extra_size().w;\n        if available_width <= 0. {\n            // Nowhere to expand.\n            return;\n        }\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n\n        if !counted_non_active_column {\n            // Only the active column was fully on-screen (maybe it's the only column), so we're\n            // about to set its width to 100% of the working area. Let's do it via\n            // toggle_full_width() as it lets you back out of it more intuitively.\n            col.toggle_full_width();\n            return;\n        }\n\n        let active_width = self.data[self.active_column_idx].width;\n        col.width = ColumnWidth::Fixed(active_width + available_width);\n        col.preset_width_idx = None;\n        col.is_full_width = false;\n        col.update_tile_sizes(true);\n\n        // Put the leftmost window into the view.\n        let new_view_x = leftmost_col_x.unwrap() - gap - working_x;\n        self.animate_view_offset(self.active_column_idx, new_view_x - active_col_x.unwrap());\n        // Just in case.\n        self.animate_view_offset_to_column(None, self.active_column_idx, None);\n    }\n\n    pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) -> bool {\n        let mut col_idx = self\n            .columns\n            .iter()\n            .position(|col| col.contains(window))\n            .unwrap();\n\n        if is_fullscreen == self.columns[col_idx].is_pending_fullscreen {\n            return false;\n        }\n\n        let mut col = &mut self.columns[col_idx];\n        let is_tabbed = col.display_mode == ColumnDisplay::Tabbed;\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n\n        if is_fullscreen && (col.tiles.len() > 1 && !is_tabbed) {\n            // This wasn't the only window in its column; extract it into a separate column.\n            self.consume_or_expel_window_right(Some(window));\n            col_idx += 1;\n            col = &mut self.columns[col_idx];\n        }\n\n        col.set_fullscreen(is_fullscreen);\n\n        // With place_within_column, the tab indicator changes the column size immediately.\n        self.data[col_idx].update(col);\n\n        true\n    }\n\n    pub fn set_maximized(&mut self, window: &W::Id, maximize: bool) -> bool {\n        let mut col_idx = self\n            .columns\n            .iter()\n            .position(|col| col.contains(window))\n            .unwrap();\n\n        if maximize == self.columns[col_idx].is_pending_maximized {\n            return false;\n        }\n\n        let mut col = &mut self.columns[col_idx];\n        let is_tabbed = col.display_mode == ColumnDisplay::Tabbed;\n\n        cancel_resize_for_column(&mut self.interactive_resize, col);\n\n        if maximize && (col.tiles.len() > 1 && !is_tabbed) {\n            // This wasn't the only window in its column; extract it into a separate column.\n            self.consume_or_expel_window_right(Some(window));\n            col_idx += 1;\n            col = &mut self.columns[col_idx];\n        }\n\n        col.set_maximized(maximize);\n\n        // With place_within_column, the tab indicator changes the column size immediately.\n        self.data[col_idx].update(col);\n\n        true\n    }\n\n    pub fn render_above_top_layer(&self) -> bool {\n        // Render above the top layer if we're on a fullscreen window and the view is stationary.\n        if self.columns.is_empty() {\n            return false;\n        }\n\n        if !self.view_offset.is_static() {\n            return false;\n        }\n\n        self.columns[self.active_column_idx]\n            .sizing_mode()\n            .is_fullscreen()\n    }\n\n    pub fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        target: RenderTarget,\n        focus_ring: bool,\n        push: &mut dyn FnMut(ScrollingSpaceRenderElement<R>),\n    ) {\n        let scale = Scale::from(self.scale);\n\n        // Draw the closing windows on top of the other windows.\n        let view_rect = Rectangle::new(Point::from((self.view_pos(), 0.)), self.view_size);\n        for closing in self.closing_windows.iter().rev() {\n            let elem = closing.render(renderer.as_gles_renderer(), view_rect, scale, target);\n            push(elem.into());\n        }\n\n        if self.columns.is_empty() {\n            return;\n        }\n\n        let mut first = true;\n\n        // This matches self.tiles_in_render_order().\n        let view_off = Point::from((-self.view_pos(), 0.));\n        for (col, col_x) in self.columns_in_render_order() {\n            let col_off = Point::from((col_x, 0.));\n            let col_render_off = col.render_offset();\n\n            // Draw the tab indicator on top.\n            {\n                let pos = view_off + col_off + col_render_off;\n                let pos = pos.to_physical_precise_round(scale).to_logical(scale);\n                col.tab_indicator\n                    .render(renderer, pos, &mut |elem| push(elem.into()));\n            }\n\n            for (tile, tile_off, visible) in col.tiles_in_render_order() {\n                let tile_pos =\n                    view_off + col_off + col_render_off + tile_off + tile.render_offset();\n                // Round to physical pixels.\n                let tile_pos = tile_pos.to_physical_precise_round(scale).to_logical(scale);\n\n                // And now the drawing logic.\n\n                // For the active tile (which comes first), draw the focus ring.\n                let focus_ring = focus_ring && first;\n                first = false;\n\n                // In the scrolling layout, we currently use visible only for hidden tabs in the\n                // tabbed mode. We want to animate their opacity when going in and out of tabbed\n                // mode, so we don't want to apply \"visible\" immediately. However, \"visible\" is\n                // also used for input handling, and there we *do* want to apply it immediately.\n                // So, let's just selectively ignore \"visible\" here when animating alpha.\n                let visible = visible || tile.alpha_animation.is_some();\n                if !visible {\n                    continue;\n                }\n\n                tile.render(renderer, tile_pos, focus_ring, target, &mut |elem| {\n                    push(elem.into())\n                });\n            }\n        }\n    }\n\n    pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<(&W, HitType)> {\n        // This matches self.tiles_with_render_positions().\n        let scale = self.scale;\n        let view_off = Point::from((-self.view_pos(), 0.));\n        for (col, col_x) in self.columns_in_render_order() {\n            let col_off = Point::from((col_x, 0.));\n            let col_render_off = col.render_offset();\n\n            // Hit the tab indicator.\n            if col.display_mode == ColumnDisplay::Tabbed && col.sizing_mode().is_normal() {\n                let col_pos = view_off + col_off + col_render_off;\n                let col_pos = col_pos.to_physical_precise_round(scale).to_logical(scale);\n\n                if let Some(idx) = col.tab_indicator.hit(\n                    col.tab_indicator_area(),\n                    col.tiles.len(),\n                    scale,\n                    pos - col_pos,\n                ) {\n                    let hit = HitType::Activate {\n                        is_tab_indicator: true,\n                    };\n                    return Some((col.tiles[idx].window(), hit));\n                }\n            }\n\n            for (tile, tile_off, visible) in col.tiles_in_render_order() {\n                if !visible {\n                    continue;\n                }\n\n                let tile_pos =\n                    view_off + col_off + col_render_off + tile_off + tile.render_offset();\n                // Round to physical pixels.\n                let tile_pos = tile_pos.to_physical_precise_round(scale).to_logical(scale);\n\n                if let Some(rv) = HitType::hit_tile(tile, tile_pos, pos) {\n                    return Some(rv);\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn view_offset_gesture_begin(&mut self, is_touchpad: bool) {\n        if self.columns.is_empty() {\n            return;\n        }\n\n        if self.interactive_resize.is_some() {\n            return;\n        }\n\n        let gesture = ViewGesture {\n            current_view_offset: self.view_offset.current(),\n            animation: None,\n            tracker: SwipeTracker::new(),\n            delta_from_tracker: self.view_offset.current(),\n            stationary_view_offset: self.view_offset.stationary(),\n            is_touchpad,\n            dnd_last_event_time: None,\n            dnd_nonzero_start_time: None,\n        };\n        self.view_offset = ViewOffset::Gesture(gesture);\n    }\n\n    pub fn dnd_scroll_gesture_begin(&mut self) {\n        if let ViewOffset::Gesture(ViewGesture {\n            dnd_last_event_time: Some(_),\n            ..\n        }) = &self.view_offset\n        {\n            // Already active.\n            return;\n        }\n\n        let gesture = ViewGesture {\n            current_view_offset: self.view_offset.current(),\n            animation: None,\n            tracker: SwipeTracker::new(),\n            delta_from_tracker: self.view_offset.current(),\n            stationary_view_offset: self.view_offset.stationary(),\n            is_touchpad: false,\n            dnd_last_event_time: Some(self.clock.now_unadjusted()),\n            dnd_nonzero_start_time: None,\n        };\n        self.view_offset = ViewOffset::Gesture(gesture);\n\n        self.interactive_resize = None;\n    }\n\n    pub fn view_offset_gesture_update(\n        &mut self,\n        delta_x: f64,\n        timestamp: Duration,\n        is_touchpad: bool,\n    ) -> Option<bool> {\n        let ViewOffset::Gesture(gesture) = &mut self.view_offset else {\n            return None;\n        };\n\n        if gesture.is_touchpad != is_touchpad || gesture.dnd_last_event_time.is_some() {\n            return None;\n        }\n\n        gesture.tracker.push(delta_x, timestamp);\n\n        let norm_factor = if gesture.is_touchpad {\n            self.working_area.size.w / VIEW_GESTURE_WORKING_AREA_MOVEMENT\n        } else {\n            1.\n        };\n        let pos = gesture.tracker.pos() * norm_factor;\n        let view_offset = pos + gesture.delta_from_tracker;\n        gesture.current_view_offset = view_offset;\n\n        Some(true)\n    }\n\n    pub fn dnd_scroll_gesture_scroll(&mut self, delta: f64) -> bool {\n        let ViewOffset::Gesture(gesture) = &mut self.view_offset else {\n            return false;\n        };\n\n        let Some(last_time) = gesture.dnd_last_event_time else {\n            // Not a DnD scroll.\n            return false;\n        };\n\n        let config = &self.options.gestures.dnd_edge_view_scroll;\n\n        let now = self.clock.now_unadjusted();\n        gesture.dnd_last_event_time = Some(now);\n\n        if delta == 0. {\n            // We're outside the scrolling zone.\n            gesture.dnd_nonzero_start_time = None;\n            return false;\n        }\n\n        let nonzero_start = *gesture.dnd_nonzero_start_time.get_or_insert(now);\n\n        // Delay starting the gesture a bit to avoid unwanted movement when dragging across\n        // monitors.\n        let delay = Duration::from_millis(u64::from(config.delay_ms));\n        if now.saturating_sub(nonzero_start) < delay {\n            return true;\n        }\n\n        let time_delta = now.saturating_sub(last_time).as_secs_f64();\n\n        let delta = delta * time_delta * config.max_speed;\n\n        gesture.tracker.push(delta, now);\n\n        let view_offset = gesture.tracker.pos() + gesture.delta_from_tracker;\n\n        // Clamp it so that it doesn't go too much out of bounds.\n        let (leftmost, rightmost) = if self.columns.is_empty() {\n            (0., 0.)\n        } else {\n            let gaps = self.options.layout.gaps;\n\n            let mut leftmost = -self.working_area.size.w;\n\n            let last_col_idx = self.columns.len() - 1;\n            let last_col_x = self\n                .columns\n                .iter()\n                .take(last_col_idx)\n                .fold(0., |col_x, col| col_x + col.width() + gaps);\n            let last_col_width = self.data[last_col_idx].width;\n            let mut rightmost = last_col_x + last_col_width - self.working_area.loc.x;\n\n            let active_col_x = self\n                .columns\n                .iter()\n                .take(self.active_column_idx)\n                .fold(0., |col_x, col| col_x + col.width() + gaps);\n            leftmost -= active_col_x;\n            rightmost -= active_col_x;\n\n            (leftmost, rightmost)\n        };\n        let min_offset = f64::min(leftmost, rightmost);\n        let max_offset = f64::max(leftmost, rightmost);\n        let clamped_offset = view_offset.clamp(min_offset, max_offset);\n\n        gesture.delta_from_tracker += clamped_offset - view_offset;\n        gesture.current_view_offset = clamped_offset;\n        true\n    }\n\n    pub fn view_offset_gesture_end(&mut self, is_touchpad: Option<bool>) -> bool {\n        let ViewOffset::Gesture(gesture) = &mut self.view_offset else {\n            return false;\n        };\n\n        if is_touchpad.is_some_and(|x| gesture.is_touchpad != x) {\n            return false;\n        }\n\n        // We do not handle cancelling, just like GNOME Shell doesn't. For this gesture, proper\n        // cancelling would require keeping track of the original active column, and then updating\n        // it in all the right places (adding columns, removing columns, etc.) -- quite a bit of\n        // effort and bug potential.\n\n        // Take into account any idle time between the last event and now.\n        let now = self.clock.now_unadjusted();\n        gesture.tracker.push(0., now);\n\n        let norm_factor = if gesture.is_touchpad {\n            self.working_area.size.w / VIEW_GESTURE_WORKING_AREA_MOVEMENT\n        } else {\n            1.\n        };\n        let velocity = gesture.tracker.velocity() * norm_factor;\n        let pos = gesture.tracker.pos() * norm_factor;\n        let current_view_offset = pos + gesture.delta_from_tracker;\n\n        if self.columns.is_empty() {\n            self.view_offset = ViewOffset::Static(current_view_offset);\n            return true;\n        }\n\n        // Figure out where the gesture would stop after deceleration.\n        let end_pos = gesture.tracker.projected_end_pos() * norm_factor;\n        let target_view_offset = end_pos + gesture.delta_from_tracker;\n\n        // Compute the snapping points. These are where the view aligns with column boundaries on\n        // either side.\n        struct Snap {\n            // View position relative to x = 0 (the first column).\n            view_pos: f64,\n            // Column to activate for this snapping point.\n            col_idx: usize,\n        }\n\n        let mut snapping_points = Vec::new();\n\n        if self.is_centering_focused_column() {\n            let mut col_x = 0.;\n            for (col_idx, col) in self.columns.iter().enumerate() {\n                let col_w = col.width();\n                let mode = col.sizing_mode();\n\n                let area = if mode.is_maximized() {\n                    self.parent_area\n                } else {\n                    self.working_area\n                };\n\n                let left_strut = area.loc.x;\n\n                let view_pos = if mode.is_fullscreen() {\n                    col_x\n                } else if area.size.w <= col_w {\n                    col_x - left_strut\n                } else {\n                    col_x - (area.size.w - col_w) / 2. - left_strut\n                };\n                snapping_points.push(Snap { view_pos, col_idx });\n\n                col_x += col_w + self.options.layout.gaps;\n            }\n        } else {\n            let center_on_overflow = matches!(\n                self.options.layout.center_focused_column,\n                CenterFocusedColumn::OnOverflow\n            );\n\n            let view_width = self.view_size.w;\n            let gaps = self.options.layout.gaps;\n\n            let snap_points =\n                |col_x, col: &Column<W>, prev_col_w: Option<f64>, next_col_w: Option<f64>| {\n                    let col_w = col.width();\n                    let mode = col.sizing_mode();\n\n                    let area = if mode.is_maximized() {\n                        self.parent_area\n                    } else {\n                        self.working_area\n                    };\n\n                    let left_strut = area.loc.x;\n                    let right_strut = self.view_size.w - area.size.w - area.loc.x;\n\n                    // Normal columns align with the working area, but fullscreen columns align with\n                    // the view size.\n                    if mode.is_fullscreen() {\n                        let left = col_x;\n                        let right = left + col_w;\n                        (left, right)\n                    } else {\n                        // Logic from compute_new_view_offset.\n                        let padding = if mode.is_maximized() {\n                            0.\n                        } else {\n                            ((area.size.w - col_w) / 2.).clamp(0., gaps)\n                        };\n\n                        let center = if area.size.w <= col_w {\n                            col_x - left_strut\n                        } else {\n                            col_x - (area.size.w - col_w) / 2. - left_strut\n                        };\n                        let is_overflowing = |adj_col_w: Option<f64>| {\n                            center_on_overflow\n                                && adj_col_w\n                                    .filter(|adj_col_w| {\n                                        // NOTE: This logic won't work entirely correctly with small\n                                        // fixed-size maximized windows (they have a different area\n                                        // and padding).\n                                        center_on_overflow\n                                            && adj_col_w + 3.0 * gaps + col_w > area.size.w\n                                    })\n                                    .is_some()\n                        };\n\n                        let left = if is_overflowing(next_col_w) {\n                            center\n                        } else {\n                            col_x - padding - left_strut\n                        };\n                        let right = if is_overflowing(prev_col_w) {\n                            center + view_width\n                        } else {\n                            col_x + col_w + padding + right_strut\n                        };\n                        (left, right)\n                    }\n                };\n\n            // Prevent the gesture from snapping further than the first/last column, as this is\n            // generally undesired.\n            //\n            // It's ok if leftmost_snap is > rightmost_snap (this happens if the columns on a\n            // workspace total up to less than the workspace width).\n\n            // The first column's left snap isn't actually guaranteed to be the *leftmost* snap.\n            // With weird enough left strut and perhaps a maximized small fixed-size window, you\n            // can make the second window's left snap be further to the left than the first\n            // window's. The same goes for the rightmost snap.\n            //\n            // This isn't actually a big problem because it's very much an obscure edge case. Just\n            // need to make sure the code doesn't panic when that happens.\n            let leftmost_snap = snap_points(\n                0.,\n                &self.columns[0],\n                None,\n                self.columns.get(1).map(|c| c.width()),\n            )\n            .0;\n            let last_col_idx = self.columns.len() - 1;\n            let last_col_x = self\n                .columns\n                .iter()\n                .take(last_col_idx)\n                .fold(0., |col_x, col| col_x + col.width() + gaps);\n            let rightmost_snap = snap_points(\n                last_col_x,\n                &self.columns[last_col_idx],\n                last_col_idx\n                    .checked_sub(1)\n                    .and_then(|idx| self.columns.get(idx).map(|c| c.width())),\n                None,\n            )\n            .1 - view_width;\n\n            snapping_points.push(Snap {\n                view_pos: leftmost_snap,\n                col_idx: 0,\n            });\n            snapping_points.push(Snap {\n                view_pos: rightmost_snap,\n                col_idx: last_col_idx,\n            });\n\n            let mut push = |col_idx, left, right| {\n                if leftmost_snap < left && left < rightmost_snap {\n                    snapping_points.push(Snap {\n                        view_pos: left,\n                        col_idx,\n                    });\n                }\n\n                let right = right - view_width;\n                if leftmost_snap < right && right < rightmost_snap {\n                    snapping_points.push(Snap {\n                        view_pos: right,\n                        col_idx,\n                    });\n                }\n            };\n\n            let mut col_x = 0.;\n            for (col_idx, col) in self.columns.iter().enumerate() {\n                let (left, right) = snap_points(\n                    col_x,\n                    col,\n                    col_idx\n                        .checked_sub(1)\n                        .and_then(|idx| self.columns.get(idx).map(|c| c.width())),\n                    self.columns.get(col_idx + 1).map(|c| c.width()),\n                );\n                push(col_idx, left, right);\n\n                col_x += col.width() + gaps;\n            }\n        }\n\n        // Find the closest snapping point.\n        snapping_points.sort_by_key(|snap| NotNan::new(snap.view_pos).unwrap());\n\n        let active_col_x = self.column_x(self.active_column_idx);\n        let target_view_pos = active_col_x + target_view_offset;\n        let target_snap = snapping_points\n            .iter()\n            .min_by_key(|snap| NotNan::new((snap.view_pos - target_view_pos).abs()).unwrap())\n            .unwrap();\n\n        let mut new_col_idx = target_snap.col_idx;\n\n        if !self.is_centering_focused_column() {\n            // Focus the furthest window towards the direction of the gesture.\n            if target_view_offset >= current_view_offset {\n                for col_idx in (new_col_idx + 1)..self.columns.len() {\n                    let col = &self.columns[col_idx];\n                    let col_x = self.column_x(col_idx);\n                    let col_w = col.width();\n                    let mode = col.sizing_mode();\n\n                    let area = if mode.is_maximized() {\n                        self.parent_area\n                    } else {\n                        self.working_area\n                    };\n\n                    let left_strut = area.loc.x;\n\n                    if mode.is_fullscreen() {\n                        if target_snap.view_pos + self.view_size.w < col_x + col_w {\n                            break;\n                        }\n                    } else {\n                        let padding = if mode.is_maximized() {\n                            0.\n                        } else {\n                            ((area.size.w - col_w) / 2.).clamp(0., self.options.layout.gaps)\n                        };\n\n                        if target_snap.view_pos + left_strut + area.size.w < col_x + col_w + padding\n                        {\n                            break;\n                        }\n                    }\n\n                    new_col_idx = col_idx;\n                }\n            } else {\n                for col_idx in (0..new_col_idx).rev() {\n                    let col = &self.columns[col_idx];\n                    let col_x = self.column_x(col_idx);\n                    let col_w = col.width();\n                    let mode = col.sizing_mode();\n\n                    let area = if mode.is_maximized() {\n                        self.parent_area\n                    } else {\n                        self.working_area\n                    };\n\n                    let left_strut = area.loc.x;\n\n                    if mode.is_fullscreen() {\n                        if col_x < target_snap.view_pos {\n                            break;\n                        }\n                    } else {\n                        let padding = if mode.is_maximized() {\n                            0.\n                        } else {\n                            ((area.size.w - col_w) / 2.).clamp(0., self.options.layout.gaps)\n                        };\n\n                        if col_x - padding < target_snap.view_pos + left_strut {\n                            break;\n                        }\n                    }\n\n                    new_col_idx = col_idx;\n                }\n            }\n        }\n\n        let new_col_x = self.column_x(new_col_idx);\n        let delta = active_col_x - new_col_x;\n\n        if self.active_column_idx != new_col_idx {\n            self.view_offset_to_restore = None;\n        }\n\n        self.active_column_idx = new_col_idx;\n\n        let target_view_offset = target_snap.view_pos - new_col_x;\n\n        self.view_offset = ViewOffset::Animation(Animation::new(\n            self.clock.clone(),\n            current_view_offset + delta,\n            target_view_offset,\n            velocity,\n            self.options.animations.horizontal_view_movement.0,\n        ));\n\n        // HACK: deal with things like snapping to the right edge of a larger-than-view window.\n        self.animate_view_offset_to_column(None, new_col_idx, None);\n\n        true\n    }\n\n    pub fn dnd_scroll_gesture_end(&mut self) {\n        let ViewOffset::Gesture(gesture) = &mut self.view_offset else {\n            return;\n        };\n\n        if gesture.dnd_last_event_time.is_some() && gesture.tracker.pos() == 0. {\n            // DnD didn't scroll anything, so preserve the current view position (rather than\n            // snapping the window).\n\n            // If there's an ongoing animation within the gesture (e.g. from a window being removed\n            // during DnD), preserve it.\n            if let Some(mut anim) = gesture.animation.take() {\n                anim.offset(gesture.current_view_offset);\n                self.view_offset = ViewOffset::Animation(anim);\n            } else {\n                self.view_offset = ViewOffset::Static(gesture.delta_from_tracker);\n            }\n\n            if !self.columns.is_empty() {\n                // Just in case, make sure the active window remains on screen.\n                self.animate_view_offset_to_column(None, self.active_column_idx, None);\n            }\n            return;\n        }\n\n        self.view_offset_gesture_end(None);\n    }\n\n    pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {\n        if self.interactive_resize.is_some() {\n            return false;\n        }\n\n        let col = self\n            .columns\n            .iter_mut()\n            .find(|col| col.contains(&window))\n            .unwrap();\n\n        if !col.pending_sizing_mode().is_normal() {\n            return false;\n        }\n\n        let tile = col\n            .tiles\n            .iter_mut()\n            .find(|tile| tile.window().id() == &window)\n            .unwrap();\n\n        let original_window_size = tile.window_size();\n\n        let resize = InteractiveResize {\n            window,\n            original_window_size,\n            data: InteractiveResizeData { edges },\n        };\n        self.interactive_resize = Some(resize);\n\n        self.view_offset.stop_anim_and_gesture();\n\n        true\n    }\n\n    pub fn interactive_resize_update(\n        &mut self,\n        window: &W::Id,\n        delta: Point<f64, Logical>,\n    ) -> bool {\n        let Some(resize) = &self.interactive_resize else {\n            return false;\n        };\n\n        if window != &resize.window {\n            return false;\n        }\n\n        let is_centering = self.is_centering_focused_column();\n\n        let col = self\n            .columns\n            .iter_mut()\n            .find(|col| col.contains(window))\n            .unwrap();\n\n        let tile_idx = col\n            .tiles\n            .iter()\n            .position(|tile| tile.window().id() == window)\n            .unwrap();\n\n        if resize.data.edges.intersects(ResizeEdge::LEFT_RIGHT) {\n            let mut dx = delta.x;\n            if resize.data.edges.contains(ResizeEdge::LEFT) {\n                dx = -dx;\n            };\n\n            if is_centering {\n                dx *= 2.;\n            }\n\n            let window_width = (resize.original_window_size.w + dx).round() as i32;\n            col.set_column_width(SizeChange::SetFixed(window_width), Some(tile_idx), false);\n        }\n\n        if resize.data.edges.intersects(ResizeEdge::TOP_BOTTOM) {\n            // Prevent the simplest case of weird resizing (top edge when this is the topmost\n            // window).\n            if !(resize.data.edges.contains(ResizeEdge::TOP) && tile_idx == 0) {\n                let mut dy = delta.y;\n                if resize.data.edges.contains(ResizeEdge::TOP) {\n                    dy = -dy;\n                };\n\n                // FIXME: some smarter height distribution would be nice here so that vertical\n                // resizes work as expected in more cases.\n\n                let window_height = (resize.original_window_size.h + dy).round() as i32;\n                col.set_window_height(SizeChange::SetFixed(window_height), Some(tile_idx), false);\n            }\n        }\n\n        true\n    }\n\n    pub fn interactive_resize_end(&mut self, window: Option<&W::Id>) {\n        let Some(resize) = &self.interactive_resize else {\n            return;\n        };\n\n        if let Some(window) = window {\n            if window != &resize.window {\n                return;\n            }\n\n            // Animate the active window into view right away.\n            if self.columns[self.active_column_idx].contains(window) {\n                self.animate_view_offset_to_column(None, self.active_column_idx, None);\n            }\n        }\n\n        self.interactive_resize = None;\n    }\n\n    pub fn refresh(&mut self, is_active: bool, is_focused: bool) {\n        for (col_idx, col) in self.columns.iter_mut().enumerate() {\n            let mut col_resize_data = None;\n            if let Some(resize) = &self.interactive_resize {\n                if col.contains(&resize.window) {\n                    col_resize_data = Some(resize.data);\n                }\n            }\n\n            let is_tabbed = col.display_mode == ColumnDisplay::Tabbed;\n            let extra_size = col.extra_size();\n\n            // If transactions are disabled, also disable combined throttling, for more intuitive\n            // behavior. In tabbed display mode, only one window is visible, so individual\n            // throttling makes more sense.\n            let individual_throttling = self.options.disable_transactions || is_tabbed;\n\n            let intent = if self.options.disable_resize_throttling {\n                ConfigureIntent::CanSend\n            } else if individual_throttling {\n                // In this case, we don't use combined throttling, but rather compute throttling\n                // individually below.\n                ConfigureIntent::CanSend\n            } else {\n                col.tiles\n                    .iter()\n                    .fold(ConfigureIntent::NotNeeded, |intent, tile| {\n                        match (intent, tile.window().configure_intent()) {\n                            (_, ConfigureIntent::ShouldSend) => ConfigureIntent::ShouldSend,\n                            (ConfigureIntent::NotNeeded, tile_intent) => tile_intent,\n                            (ConfigureIntent::CanSend, ConfigureIntent::Throttled) => {\n                                ConfigureIntent::Throttled\n                            }\n                            (intent, _) => intent,\n                        }\n                    })\n            };\n\n            for (tile_idx, tile) in col.tiles.iter_mut().enumerate() {\n                let win = tile.window_mut();\n\n                let active_in_column = col.active_tile_idx == tile_idx;\n                win.set_active_in_column(active_in_column);\n                win.set_floating(false);\n\n                let mut active = is_active && self.active_column_idx == col_idx;\n                if self.options.deactivate_unfocused_windows {\n                    active &= active_in_column && is_focused;\n                } else {\n                    // In tabbed mode, all tabs have activated state to reduce unnecessary\n                    // animations when switching tabs.\n                    active &= active_in_column || is_tabbed;\n                }\n                win.set_activated(active);\n\n                win.set_interactive_resize(col_resize_data);\n\n                let border_config = self.options.layout.border.merged_with(&win.rules().border);\n                let bounds = compute_toplevel_bounds(\n                    border_config,\n                    self.working_area.size,\n                    extra_size,\n                    self.options.layout.gaps,\n                );\n                win.set_bounds(bounds);\n\n                let intent = if individual_throttling {\n                    win.configure_intent()\n                } else {\n                    intent\n                };\n\n                if matches!(\n                    intent,\n                    ConfigureIntent::CanSend | ConfigureIntent::ShouldSend\n                ) {\n                    win.send_pending_configure();\n                }\n\n                win.refresh();\n            }\n        }\n    }\n\n    #[cfg(test)]\n    pub fn view_size(&self) -> Size<f64, Logical> {\n        self.view_size\n    }\n\n    #[cfg(test)]\n    pub fn parent_area(&self) -> Rectangle<f64, Logical> {\n        self.parent_area\n    }\n\n    #[cfg(test)]\n    pub fn clock(&self) -> &Clock {\n        &self.clock\n    }\n\n    #[cfg(test)]\n    pub fn options(&self) -> &Rc<Options> {\n        &self.options\n    }\n\n    #[cfg(test)]\n    pub fn active_column_idx(&self) -> usize {\n        self.active_column_idx\n    }\n\n    #[cfg(test)]\n    pub(super) fn view_offset(&self) -> &ViewOffset {\n        &self.view_offset\n    }\n\n    #[cfg(test)]\n    pub fn verify_invariants(&self) {\n        assert!(self.view_size.w > 0.);\n        assert!(self.view_size.h > 0.);\n        assert!(self.scale > 0.);\n        assert!(self.scale.is_finite());\n        assert_eq!(self.columns.len(), self.data.len());\n        assert_eq!(\n            self.working_area,\n            compute_working_area(self.parent_area, self.scale, self.options.layout.struts)\n        );\n\n        if !self.columns.is_empty() {\n            assert!(self.active_column_idx < self.columns.len());\n\n            for (column, data) in zip(&self.columns, &self.data) {\n                assert!(Rc::ptr_eq(&self.options, &column.options));\n                assert_eq!(self.clock, column.clock);\n                assert_eq!(self.scale, column.scale);\n                column.verify_invariants();\n\n                let mut data2 = *data;\n                data2.update(column);\n                assert_eq!(data, &data2, \"column data must be up to date\");\n            }\n\n            let col = &self.columns[self.active_column_idx];\n\n            if self.view_offset_to_restore.is_some() {\n                assert!(\n                    !col.sizing_mode().is_normal(),\n                    \"when view_offset_to_restore is set, \\\n                     the active column must be fullscreen or maximized\"\n                );\n            }\n        }\n\n        if let Some(resize) = &self.interactive_resize {\n            assert!(\n                self.columns\n                    .iter()\n                    .flat_map(|col| &col.tiles)\n                    .any(|tile| tile.window().id() == &resize.window),\n                \"interactive resize window must be present in the layout\"\n            );\n        }\n    }\n}\n\nimpl ViewOffset {\n    /// Returns the current view offset.\n    pub fn current(&self) -> f64 {\n        match self {\n            ViewOffset::Static(offset) => *offset,\n            ViewOffset::Animation(anim) => anim.value(),\n            ViewOffset::Gesture(gesture) => {\n                gesture.current_view_offset\n                    + gesture.animation.as_ref().map_or(0., |anim| anim.value())\n            }\n        }\n    }\n\n    /// Returns the target view offset suitable for computing the new view offset.\n    pub fn target(&self) -> f64 {\n        match self {\n            ViewOffset::Static(offset) => *offset,\n            ViewOffset::Animation(anim) => anim.to(),\n            // This can be used for example if a gesture is interrupted.\n            ViewOffset::Gesture(gesture) => gesture.current_view_offset,\n        }\n    }\n\n    /// Returns a view offset value suitable for saving and later restoration.\n    ///\n    /// This means that it shouldn't return an in-progress animation or gesture value.\n    fn stationary(&self) -> f64 {\n        match self {\n            ViewOffset::Static(offset) => *offset,\n            // For animations we can return the final value.\n            ViewOffset::Animation(anim) => anim.to(),\n            ViewOffset::Gesture(gesture) => gesture.stationary_view_offset,\n        }\n    }\n\n    pub fn is_static(&self) -> bool {\n        matches!(self, Self::Static(_))\n    }\n\n    pub fn is_gesture(&self) -> bool {\n        matches!(self, Self::Gesture(_))\n    }\n\n    pub fn is_dnd_scroll(&self) -> bool {\n        matches!(&self, ViewOffset::Gesture(gesture) if gesture.dnd_last_event_time.is_some())\n    }\n\n    pub fn is_animation_ongoing(&self) -> bool {\n        match self {\n            ViewOffset::Static(_) => false,\n            ViewOffset::Animation(_) => true,\n            ViewOffset::Gesture(gesture) => gesture.animation.is_some(),\n        }\n    }\n\n    pub fn offset(&mut self, delta: f64) {\n        match self {\n            ViewOffset::Static(offset) => *offset += delta,\n            ViewOffset::Animation(anim) => anim.offset(delta),\n            ViewOffset::Gesture(gesture) => {\n                gesture.stationary_view_offset += delta;\n                gesture.delta_from_tracker += delta;\n                gesture.current_view_offset += delta;\n            }\n        }\n    }\n\n    pub fn cancel_gesture(&mut self) {\n        if let ViewOffset::Gesture(gesture) = self {\n            *self = ViewOffset::Static(gesture.current_view_offset);\n        }\n    }\n\n    pub fn stop_anim_and_gesture(&mut self) {\n        *self = ViewOffset::Static(self.current());\n    }\n}\n\nimpl ViewGesture {\n    fn animate_from(&mut self, from: f64, clock: Clock, config: niri_config::Animation) {\n        let current = self.animation.as_ref().map_or(0., Animation::value);\n        self.animation = Some(Animation::new(clock, from + current, 0., 0., config));\n    }\n}\n\nimpl ColumnData {\n    pub fn new<W: LayoutElement>(column: &Column<W>) -> Self {\n        let mut rv = Self { width: 0. };\n        rv.update(column);\n        rv\n    }\n\n    pub fn update<W: LayoutElement>(&mut self, column: &Column<W>) {\n        self.width = column.width();\n    }\n}\n\nimpl TileData {\n    pub fn new<W: LayoutElement>(tile: &Tile<W>, height: WindowHeight) -> Self {\n        let mut rv = Self {\n            height,\n            size: Size::default(),\n            interactively_resizing_by_left_edge: false,\n        };\n        rv.update(tile);\n        rv\n    }\n\n    pub fn update<W: LayoutElement>(&mut self, tile: &Tile<W>) {\n        self.size = tile.tile_size();\n        self.interactively_resizing_by_left_edge = tile\n            .window()\n            .interactive_resize_data()\n            .is_some_and(|data| data.edges.contains(ResizeEdge::LEFT));\n    }\n}\n\nimpl From<PresetSize> for ColumnWidth {\n    fn from(value: PresetSize) -> Self {\n        match value {\n            PresetSize::Proportion(p) => Self::Proportion(p.clamp(0., 10000.)),\n            PresetSize::Fixed(f) => Self::Fixed(f64::from(f.clamp(1, 100000))),\n        }\n    }\n}\n\nimpl WindowHeight {\n    const fn auto_1() -> Self {\n        Self::Auto { weight: 1. }\n    }\n}\n\nimpl<W: LayoutElement> Column<W> {\n    #[allow(clippy::too_many_arguments)]\n    fn new_with_tile(\n        tile: Tile<W>,\n        view_size: Size<f64, Logical>,\n        working_area: Rectangle<f64, Logical>,\n        parent_area: Rectangle<f64, Logical>,\n        scale: f64,\n        width: ColumnWidth,\n        is_full_width: bool,\n    ) -> Self {\n        let options = tile.options.clone();\n\n        let display_mode = tile\n            .window()\n            .rules()\n            .default_column_display\n            .unwrap_or(options.layout.default_column_display);\n\n        // Try to match width to a preset width. Consider the following case: a terminal (foot)\n        // sizes itself to the terminal grid. We open it with default-column-width 0.5. It shrinks\n        // by a few pixels to evenly match the terminal grid. Then we press\n        // switch-preset-column-width intending to go to proportion 0.667, but the preset width\n        // matching code picks the proportion 0.5 preset because it's the next smallest width after\n        // the current foot's window width. Effectively, this makes the first\n        // switch-preset-column-width press ignored.\n        //\n        // However, here, we do know that width = proportion 0.5 (regardless of what the window\n        // opened with), and we can match it to a preset right away, if one exists.\n        let preset_width_idx = options\n            .layout\n            .preset_column_widths\n            .iter()\n            .position(|preset| width == ColumnWidth::from(*preset));\n\n        let mut rv = Self {\n            tiles: vec![],\n            data: vec![],\n            active_tile_idx: 0,\n            width,\n            preset_width_idx,\n            is_full_width,\n            is_pending_maximized: false,\n            is_pending_fullscreen: false,\n            display_mode,\n            tab_indicator: TabIndicator::new(options.layout.tab_indicator),\n            move_animation: None,\n            view_size,\n            working_area,\n            parent_area,\n            scale,\n            clock: tile.clock.clone(),\n            options,\n        };\n\n        let pending_sizing_mode = tile.window().pending_sizing_mode();\n\n        rv.add_tile_at(0, tile);\n\n        match pending_sizing_mode {\n            SizingMode::Normal => (),\n            SizingMode::Maximized => rv.set_maximized(true),\n            SizingMode::Fullscreen => rv.set_fullscreen(true),\n        }\n\n        // Animate the tab indicator for new columns.\n        if display_mode == ColumnDisplay::Tabbed\n            && !rv.options.layout.tab_indicator.hide_when_single_tab\n            && rv.sizing_mode().is_normal()\n        {\n            // Usually new columns are created together with window movement actions. For new\n            // windows, we handle that in start_open_animation().\n            rv.tab_indicator\n                .start_open_animation(rv.clock.clone(), rv.options.animations.window_movement.0);\n        }\n\n        rv\n    }\n\n    fn update_config(\n        &mut self,\n        view_size: Size<f64, Logical>,\n        working_area: Rectangle<f64, Logical>,\n        parent_area: Rectangle<f64, Logical>,\n        scale: f64,\n        options: Rc<Options>,\n    ) {\n        let mut update_sizes = false;\n\n        if self.view_size != view_size\n            || self.working_area != working_area\n            || self.parent_area != parent_area\n        {\n            update_sizes = true;\n        }\n\n        // If preset widths changed, clear our stored preset index.\n        if self.options.layout.preset_column_widths != options.layout.preset_column_widths {\n            self.preset_width_idx = None;\n        }\n\n        // If preset heights changed, make our heights non-preset.\n        if self.options.layout.preset_window_heights != options.layout.preset_window_heights {\n            self.convert_heights_to_auto();\n            update_sizes = true;\n        }\n\n        if self.options.layout.gaps != options.layout.gaps {\n            update_sizes = true;\n        }\n\n        if self.options.layout.border.off != options.layout.border.off\n            || self.options.layout.border.width != options.layout.border.width\n        {\n            update_sizes = true;\n        }\n\n        if self.options.layout.tab_indicator != options.layout.tab_indicator {\n            update_sizes = true;\n        }\n\n        for (tile, data) in zip(&mut self.tiles, &mut self.data) {\n            tile.update_config(view_size, scale, options.clone());\n            data.update(tile);\n        }\n\n        self.tab_indicator\n            .update_config(options.layout.tab_indicator);\n        self.view_size = view_size;\n        self.working_area = working_area;\n        self.parent_area = parent_area;\n        self.scale = scale;\n        self.options = options;\n\n        if update_sizes {\n            self.update_tile_sizes(false);\n        }\n    }\n\n    pub fn update_shaders(&mut self) {\n        for tile in &mut self.tiles {\n            tile.update_shaders();\n        }\n\n        self.tab_indicator.update_shaders();\n    }\n\n    pub fn advance_animations(&mut self) {\n        if let Some(move_) = &mut self.move_animation {\n            if move_.anim.is_done() {\n                self.move_animation = None;\n            }\n        }\n\n        for tile in &mut self.tiles {\n            tile.advance_animations();\n        }\n\n        self.tab_indicator.advance_animations();\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        self.move_animation.is_some()\n            || self.tab_indicator.are_animations_ongoing()\n            || self.tiles.iter().any(Tile::are_animations_ongoing)\n    }\n\n    pub fn are_transitions_ongoing(&self) -> bool {\n        self.move_animation.is_some()\n            || self.tab_indicator.are_animations_ongoing()\n            || self.tiles.iter().any(Tile::are_transitions_ongoing)\n    }\n\n    pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {\n        let active_idx = self.active_tile_idx;\n        for (tile_idx, (tile, tile_off)) in self.tiles_mut().enumerate() {\n            let is_active = is_active && tile_idx == active_idx;\n\n            let mut tile_view_rect = view_rect;\n            tile_view_rect.loc -= tile_off + tile.render_offset();\n            tile.update_render_elements(is_active, tile_view_rect);\n        }\n\n        let config = self.tab_indicator.config();\n        let offsets = self.tile_offsets_iter(self.data.iter().copied());\n        let tabs = zip(&self.tiles, offsets)\n            .enumerate()\n            .map(|(tile_idx, (tile, tile_off))| {\n                let is_active = tile_idx == active_idx;\n                let is_urgent = tile.window().is_urgent();\n                let tile_pos = tile_off + tile.render_offset();\n                TabInfo::from_tile(tile, tile_pos, is_active, is_urgent, &config)\n            });\n\n        // Hide the tab indicator in fullscreen. If you have it configured to overlap the window,\n        // you don't want that to happen in fullscreen. Also, laying things out correctly when the\n        // tab indicator is within the column and the column goes fullscreen, would require too\n        // many changes to the code for too little benefit (it's mostly invisible anyway).\n        let enabled = self.display_mode == ColumnDisplay::Tabbed && self.sizing_mode().is_normal();\n\n        self.tab_indicator.update_render_elements(\n            enabled,\n            self.tab_indicator_area(),\n            view_rect,\n            self.tiles.len(),\n            tabs,\n            is_active,\n            self.scale,\n        );\n    }\n\n    pub fn is_pending_fullscreen(&self) -> bool {\n        self.is_pending_fullscreen\n    }\n\n    pub fn is_pending_maximized(&self) -> bool {\n        self.is_pending_maximized\n    }\n\n    pub fn pending_sizing_mode(&self) -> SizingMode {\n        if self.is_pending_fullscreen {\n            SizingMode::Fullscreen\n        } else if self.is_pending_maximized {\n            SizingMode::Maximized\n        } else {\n            SizingMode::Normal\n        }\n    }\n\n    pub fn render_offset(&self) -> Point<f64, Logical> {\n        let mut offset = Point::from((0., 0.));\n\n        if let Some(move_) = &self.move_animation {\n            offset.x += move_.from * move_.anim.value();\n        }\n\n        offset\n    }\n\n    pub fn animate_move_from(&mut self, from_x_offset: f64) {\n        self.animate_move_from_with_config(\n            from_x_offset,\n            self.options.animations.window_movement.0,\n        );\n    }\n\n    pub fn animate_move_from_with_config(\n        &mut self,\n        from_x_offset: f64,\n        config: niri_config::Animation,\n    ) {\n        let current_offset = self\n            .move_animation\n            .as_ref()\n            .map_or(0., |move_| move_.from * move_.anim.value());\n\n        let anim = Animation::new(self.clock.clone(), 1., 0., 0., config);\n        self.move_animation = Some(MoveAnimation {\n            anim,\n            from: from_x_offset + current_offset,\n        });\n    }\n\n    pub fn offset_move_anim_current(&mut self, offset: f64) {\n        if let Some(move_) = self.move_animation.as_mut() {\n            // If the anim is almost done, there's little point trying to offset it; we can let\n            // things jump. If it turns out like a bad idea, we could restart the anim instead.\n            let value = move_.anim.value();\n            if value > 0.001 {\n                move_.from += offset / value;\n            }\n        }\n    }\n\n    /// Returns whether this column is currently fullscreen.\n    ///\n    /// As in, if it contains one currently-fullscreen tile, or in tabbed mode, if it contains at\n    /// least one currently-fullscreen tile.\n    ///\n    /// This will lag behind is_pending_fullscreen, depending on when the tiles actually respond to\n    /// the un/fullscreen request. But, it's possible for is_fullscreen() to flip instantly, for\n    /// example when consuming a fullscreen tile into a non-pending-fullscreen column.\n    ///\n    /// This controls things like:\n    ///\n    /// - whether the column draws at the top of the screen or at the start of the working area\n    /// - whether the column draws above the top layer-shell layer\n    /// - whether the tab indicator is shown\n    /// - restoring view_offset_before_fullscreen\n    ///\n    /// Edge cases to watch out for:\n    ///\n    /// - Consuming a fullscreen tile into a non-tabbed column will keep that tile fullscreen until\n    ///   it responds to the unfullscreen request. This tile may be anywhere in the column,\n    ///   including at the active position.\n    ///\n    /// - Changing a fullscreen tabbed column into normal mode is an easy way to get randomly\n    ///   delayed unfullscreening tiles in a normal column.\n    ///\n    /// - is_fullscreen() can suddenly change when consuming/expelling a fullscreen tile into/from a\n    ///   non-fullscreen column. This can influence the code that saves/restores the unfullscreen\n    ///   view offset.\n    fn sizing_mode(&self) -> SizingMode {\n        // Behaviors that we want:\n        //\n        // 1. The common case: single tile in a column. Assume no animations. Fullscreening the tile\n        //    should make it jump to the top-left of the screen only when the tile finishes\n        //    fullscreening. Similarly, unfullscreening should keep it at the top-left until the\n        //    tile had unfullscreened.\n        //\n        // 2. Unfullscreening a tabbed column with multiple tiles should restore the view offset\n        //    correctly. This means waiting for *all* tiles to unfullscreen, because otherwise the\n        //    restored view offset will immediately get overwritten by the still screen-wide column\n        //    (it uses the largest tile's width).\n        //\n        // 3. Changing a fullscreen tabbed column to normal should probably also restore the view\n        //    offset correctly. Same problem as above, but now for normal columns (since display\n        //    mode change applies instantly).\n        //\n        // The logic that satisfies these behaviors is to check if *any* tile is fullscreen.\n        let mut any_fullscreen = false;\n        let mut any_maximized = false;\n        for tile in &self.tiles {\n            match tile.sizing_mode() {\n                SizingMode::Normal => (),\n                SizingMode::Maximized => any_maximized = true,\n                SizingMode::Fullscreen => any_fullscreen = true,\n            }\n        }\n\n        if any_fullscreen {\n            SizingMode::Fullscreen\n        } else if any_maximized {\n            SizingMode::Maximized\n        } else {\n            SizingMode::Normal\n        }\n    }\n\n    pub fn contains(&self, window: &W::Id) -> bool {\n        self.tiles\n            .iter()\n            .map(Tile::window)\n            .any(|win| win.id() == window)\n    }\n\n    pub fn position(&self, window: &W::Id) -> Option<usize> {\n        self.tiles\n            .iter()\n            .map(Tile::window)\n            .position(|win| win.id() == window)\n    }\n\n    fn activate_idx(&mut self, idx: usize) -> bool {\n        if self.active_tile_idx == idx {\n            return false;\n        }\n\n        self.active_tile_idx = idx;\n\n        self.tiles[idx].ensure_alpha_animates_to_1();\n\n        true\n    }\n\n    fn activate_window(&mut self, window: &W::Id) {\n        let idx = self.position(window).unwrap();\n        self.activate_idx(idx);\n    }\n\n    fn add_tile_at(&mut self, idx: usize, mut tile: Tile<W>) {\n        tile.update_config(self.view_size, self.scale, self.options.clone());\n\n        // Inserting a tile pushes down all tiles below it, but also in always-centering mode it\n        // will affect the X position of all tiles in the column.\n        let mut prev_offsets = Vec::with_capacity(self.tiles.len() + 1);\n        prev_offsets.extend(self.tile_offsets().take(self.tiles.len()));\n\n        if self.display_mode != ColumnDisplay::Tabbed {\n            self.is_pending_fullscreen = false;\n            self.is_pending_maximized = false;\n        }\n\n        self.data\n            .insert(idx, TileData::new(&tile, WindowHeight::auto_1()));\n        self.tiles.insert(idx, tile);\n        self.update_tile_sizes(true);\n\n        // Animate tiles according to the offset changes.\n        prev_offsets.insert(idx, Point::default());\n        for (i, ((tile, offset), prev)) in zip(self.tiles_mut(), prev_offsets).enumerate() {\n            if i == idx {\n                continue;\n            }\n\n            tile.animate_move_from(prev - offset);\n        }\n    }\n\n    fn update_window(&mut self, window: &W::Id) {\n        let (tile_idx, tile) = self\n            .tiles\n            .iter_mut()\n            .enumerate()\n            .find(|(_, tile)| tile.window().id() == window)\n            .unwrap();\n\n        let prev_height = self.data[tile_idx].size.h;\n\n        tile.update_window();\n        self.data[tile_idx].update(tile);\n\n        let offset = prev_height - self.data[tile_idx].size.h;\n\n        let is_tabbed = self.display_mode == ColumnDisplay::Tabbed;\n\n        // Move windows below in tandem with resizing.\n        //\n        // FIXME: in always-centering mode, window resizing will affect the offsets of all other\n        // windows in the column, so they should all be animated. How should this interact with\n        // animated vs. non-animated resizes? For example, an animated +20 resize followed by two\n        // non-animated -10 resizes.\n        if !is_tabbed && offset != 0. {\n            if tile.resize_animation().is_some() {\n                // If there's a resize animation (that may have just started in\n                // tile.update_window()), then the apparent size change is smooth with no sudden\n                // jumps. This corresponds to adding an Y animation to tiles below.\n                for tile in &mut self.tiles[tile_idx + 1..] {\n                    tile.animate_move_y_from_with_config(\n                        offset,\n                        self.options.animations.window_resize.anim,\n                    );\n                }\n            } else {\n                // There's no resize animation, but the offset is nonzero. This could happen for\n                // example:\n                // - if the window resized on its own, which we don't animate\n                // - if the window resized by less than 10 px (the resize threshold)\n                //\n                // The latter case could also cancel an ongoing resize animation.\n                //\n                // Now, stationary tiles below shouldn't react to this offset change in any way,\n                // i.e. their apparent Y position should jump together with the resize. However,\n                // tiles below that are already animating an Y movement should offset their\n                // animations to avoid the jump.\n                //\n                // Notably, this is necessary to fix the animation jump when resizing height back\n                // and forth in quick succession (in a way that cancels the resize animation).\n                for tile in &mut self.tiles[tile_idx + 1..] {\n                    tile.offset_move_y_anim_current(offset);\n                }\n            }\n        }\n    }\n\n    /// Extra size taken up by elements in the column such as the tab indicator.\n    fn extra_size(&self) -> Size<f64, Logical> {\n        if self.display_mode == ColumnDisplay::Tabbed {\n            self.tab_indicator.extra_size(self.tiles.len(), self.scale)\n        } else {\n            Size::from((0., 0.))\n        }\n    }\n\n    fn resolve_preset_width(&self, preset: PresetSize) -> ResolvedSize {\n        let extra = self.extra_size();\n        resolve_preset_size(preset, &self.options, self.working_area.size.w, extra.w)\n    }\n\n    fn resolve_preset_height(&self, preset: PresetSize) -> ResolvedSize {\n        let extra = self.extra_size();\n        resolve_preset_size(preset, &self.options, self.working_area.size.h, extra.h)\n    }\n\n    fn resolve_column_width(&self, width: ColumnWidth) -> f64 {\n        let working_size = self.working_area.size;\n        let gaps = self.options.layout.gaps;\n        let extra = self.extra_size();\n\n        match width {\n            ColumnWidth::Proportion(proportion) => {\n                (working_size.w - gaps) * proportion - gaps - extra.w\n            }\n            ColumnWidth::Fixed(width) => width,\n        }\n    }\n\n    fn update_tile_sizes(&mut self, animate: bool) {\n        self.update_tile_sizes_with_transaction(animate, Transaction::new());\n    }\n\n    fn update_tile_sizes_with_transaction(&mut self, animate: bool, transaction: Transaction) {\n        let sizing_mode = self.pending_sizing_mode();\n        if matches!(sizing_mode, SizingMode::Fullscreen | SizingMode::Maximized) {\n            for (tile_idx, tile) in self.tiles.iter_mut().enumerate() {\n                // In tabbed mode, only the visible window participates in the transaction.\n                let is_active = tile_idx == self.active_tile_idx;\n                let transaction = if self.display_mode == ColumnDisplay::Tabbed && !is_active {\n                    None\n                } else {\n                    Some(transaction.clone())\n                };\n\n                if matches!(sizing_mode, SizingMode::Fullscreen) {\n                    tile.request_fullscreen(animate, transaction);\n                } else {\n                    tile.request_maximized(self.parent_area.size, animate, transaction);\n                }\n            }\n            return;\n        }\n\n        let is_tabbed = self.display_mode == ColumnDisplay::Tabbed;\n\n        let min_size: Vec<_> = self\n            .tiles\n            .iter()\n            .map(Tile::min_size_nonfullscreen)\n            .map(|mut size| {\n                size.w = size.w.max(1.);\n                size.h = size.h.max(1.);\n                size\n            })\n            .collect();\n        let max_size: Vec<_> = self\n            .tiles\n            .iter()\n            .map(Tile::max_size_nonfullscreen)\n            .collect();\n\n        // Compute the column width.\n        let min_width = min_size\n            .iter()\n            .map(|size| NotNan::new(size.w).unwrap())\n            .max()\n            .map(NotNan::into_inner)\n            .unwrap();\n        let max_width = max_size\n            .iter()\n            .filter_map(|size| {\n                let w = size.w;\n                if w == 0. {\n                    None\n                } else {\n                    Some(NotNan::new(w).unwrap())\n                }\n            })\n            .min()\n            .map(NotNan::into_inner)\n            .unwrap_or(f64::from(i32::MAX));\n        let max_width = f64::max(max_width, min_width);\n\n        let width = if self.is_full_width {\n            ColumnWidth::Proportion(1.)\n        } else {\n            self.width\n        };\n\n        let working_size = self.working_area.size;\n        let extra_size = self.extra_size();\n\n        let width = self.resolve_column_width(width);\n        let width = f64::max(f64::min(width, max_width), min_width);\n        let max_tile_height = working_size.h - self.options.layout.gaps * 2. - extra_size.h;\n\n        // If there are multiple windows in a column, clamp the non-auto window's height according\n        // to other windows' min sizes.\n        let mut max_non_auto_window_height = None;\n        if self.tiles.len() > 1 && !is_tabbed {\n            if let Some(non_auto_idx) = self\n                .data\n                .iter()\n                .position(|data| !matches!(data.height, WindowHeight::Auto { .. }))\n            {\n                let min_height_taken = min_size\n                    .iter()\n                    .enumerate()\n                    .filter(|(idx, _)| *idx != non_auto_idx)\n                    .map(|(_, min_size)| min_size.h + self.options.layout.gaps)\n                    .sum::<f64>();\n\n                let tile = &self.tiles[non_auto_idx];\n                let height_left = max_tile_height - min_height_taken;\n                max_non_auto_window_height = Some(f64::max(\n                    1.,\n                    tile.window_height_for_tile_height(height_left).round(),\n                ));\n            }\n        }\n\n        // Compute the tile heights. Start by converting window heights to tile heights.\n        let mut heights = zip(&self.tiles, &self.data)\n            .map(|(tile, data)| match data.height {\n                auto @ WindowHeight::Auto { .. } => auto,\n                WindowHeight::Fixed(height) => {\n                    let mut window_height = height.round().max(1.);\n                    if let Some(max) = max_non_auto_window_height {\n                        window_height = f64::min(window_height, max);\n                    } else {\n                        // In any case, clamp to the working area height.\n                        let max = tile.window_height_for_tile_height(max_tile_height).round();\n                        window_height = f64::min(window_height, max);\n                    }\n\n                    WindowHeight::Fixed(tile.tile_height_for_window_height(window_height))\n                }\n                WindowHeight::Preset(idx) => {\n                    let preset = self.options.layout.preset_window_heights[idx];\n                    let window_height = match self.resolve_preset_height(preset) {\n                        ResolvedSize::Tile(h) => tile.window_height_for_tile_height(h),\n                        ResolvedSize::Window(h) => h,\n                    };\n\n                    let mut window_height = window_height.round().clamp(1., 100000.);\n                    if let Some(max) = max_non_auto_window_height {\n                        window_height = f64::min(window_height, max);\n                    }\n\n                    let tile_height = tile.tile_height_for_window_height(window_height);\n                    WindowHeight::Fixed(tile_height)\n                }\n            })\n            .collect::<Vec<_>>();\n\n        // In tabbed display mode, fill fixed heights right away.\n        if is_tabbed {\n            // All tiles have the same height, equal to the height of the only fixed tile (if any).\n            let tabbed_height = heights\n                .iter()\n                .find_map(|h| {\n                    if let WindowHeight::Fixed(h) = h {\n                        Some(*h)\n                    } else {\n                        None\n                    }\n                })\n                .unwrap_or(max_tile_height);\n\n            // We also take min height of all tabs into account.\n            let min_height = min_size\n                .iter()\n                .map(|size| NotNan::new(size.h).unwrap())\n                .max()\n                .map(NotNan::into_inner)\n                .unwrap();\n            // But, if there's a larger-than-workspace tab, we don't want to force all tabs to that\n            // size.\n            let min_height = f64::min(max_tile_height, min_height);\n            let tabbed_height = f64::max(tabbed_height, min_height);\n\n            heights.fill(WindowHeight::Fixed(tabbed_height));\n\n            // The following logic will apply individual min/max height, etc.\n        }\n\n        let gaps_left = self.options.layout.gaps * (self.tiles.len() + 1) as f64;\n        let mut height_left = working_size.h - gaps_left;\n        let mut auto_tiles_left = self.tiles.len();\n\n        // Subtract all fixed-height tiles.\n        for (h, (min_size, max_size)) in zip(&mut heights, zip(&min_size, &max_size)) {\n            // Check if the tile has an exact height constraint.\n            if min_size.h == max_size.h {\n                *h = WindowHeight::Fixed(min_size.h);\n            }\n\n            if let WindowHeight::Fixed(h) = h {\n                if max_size.h > 0. {\n                    *h = f64::min(*h, max_size.h);\n                }\n                *h = f64::max(*h, min_size.h);\n\n                height_left -= *h;\n                auto_tiles_left -= 1;\n            }\n        }\n\n        let mut total_weight: f64 = heights\n            .iter()\n            .filter_map(|h| {\n                if let WindowHeight::Auto { weight } = *h {\n                    Some(weight)\n                } else {\n                    None\n                }\n            })\n            .sum();\n\n        // Iteratively try to distribute the remaining height, checking against tile min heights.\n        // Pick an auto height according to the current sizes, then check if it satisfies all\n        // remaining min heights. If not, allocate fixed height to those tiles and repeat the\n        // loop. On each iteration the auto height will get smaller.\n        //\n        // NOTE: we do not respect max height here. Doing so would complicate things: if the current\n        // auto height is above some tile's max height, then the auto height can become larger.\n        // Combining this with the min height loop is where the complexity appears.\n        //\n        // However, most max height uses are for fixed-size dialogs, where min height == max_height.\n        // This case is separately handled above.\n        'outer: while auto_tiles_left > 0 {\n            // Wayland requires us to round the requested size for a window to integer logical\n            // pixels, therefore we compute the remaining auto height dynamically.\n            let mut height_left_2 = height_left;\n            let mut total_weight_2 = total_weight;\n            for ((h, tile), min_size) in zip(zip(&mut heights, &self.tiles), &min_size) {\n                let weight = match *h {\n                    WindowHeight::Auto { weight } => weight,\n                    WindowHeight::Fixed(_) => continue,\n                    WindowHeight::Preset(_) => unreachable!(),\n                };\n                let factor = weight / total_weight_2;\n\n                // Compute the current auto height.\n                let mut auto = height_left_2 * factor;\n\n                // Check if the auto height satisfies the min height.\n                if min_size.h > auto {\n                    auto = min_size.h;\n                    *h = WindowHeight::Fixed(auto);\n                    height_left -= auto;\n                    total_weight -= weight;\n                    auto_tiles_left -= 1;\n\n                    // If a min height was unsatisfied, then we allocate the tile more than the\n                    // auto height, which means that the remaining auto tiles now have less height\n                    // to work with, and the loop must run again.\n                    //\n                    // If we keep going in this loop and break out later, we may allocate less\n                    // height to the subsequent tiles than would be available next iteration and\n                    // potentially trip their min height check earlier than necessary, leading to\n                    // visible snapping.\n                    continue 'outer;\n                }\n\n                auto = tile.tile_height_for_window_height(\n                    tile.window_height_for_tile_height(auto).round().max(1.),\n                );\n\n                height_left_2 -= auto;\n                total_weight_2 -= weight;\n            }\n\n            // All min heights were satisfied, fill them in.\n            for (h, tile) in zip(&mut heights, &self.tiles) {\n                let weight = match *h {\n                    WindowHeight::Auto { weight } => weight,\n                    WindowHeight::Fixed(_) => continue,\n                    WindowHeight::Preset(_) => unreachable!(),\n                };\n                let factor = weight / total_weight;\n\n                // Compute the current auto height.\n                let auto = height_left * factor;\n                let auto = tile.tile_height_for_window_height(\n                    tile.window_height_for_tile_height(auto).round().max(1.),\n                );\n\n                *h = WindowHeight::Fixed(auto);\n                height_left -= auto;\n                total_weight -= weight;\n                auto_tiles_left -= 1;\n            }\n\n            assert_eq!(auto_tiles_left, 0);\n        }\n\n        for (tile_idx, (tile, h)) in zip(&mut self.tiles, heights).enumerate() {\n            let WindowHeight::Fixed(height) = h else {\n                unreachable!()\n            };\n\n            let size = Size::from((width, height));\n\n            // In tabbed mode, only the visible window participates in the transaction.\n            let is_active = tile_idx == self.active_tile_idx;\n            let transaction = if self.display_mode == ColumnDisplay::Tabbed && !is_active {\n                None\n            } else {\n                Some(transaction.clone())\n            };\n\n            tile.request_tile_size(size, animate, transaction);\n        }\n    }\n\n    fn width(&self) -> f64 {\n        let mut tiles_width = self\n            .data\n            .iter()\n            .map(|data| NotNan::new(data.size.w).unwrap())\n            .max()\n            .map(NotNan::into_inner)\n            .unwrap();\n\n        if self.display_mode == ColumnDisplay::Tabbed && self.sizing_mode().is_normal() {\n            let extra_size = self.tab_indicator.extra_size(self.tiles.len(), self.scale);\n            tiles_width += extra_size.w;\n        }\n\n        tiles_width\n    }\n\n    fn focus_index(&mut self, index: u8) {\n        let idx = min(usize::from(index.saturating_sub(1)), self.tiles.len() - 1);\n        self.activate_idx(idx);\n    }\n\n    fn focus_up(&mut self) -> bool {\n        self.activate_idx(self.active_tile_idx.saturating_sub(1))\n    }\n\n    fn focus_down(&mut self) -> bool {\n        self.activate_idx(min(self.active_tile_idx + 1, self.tiles.len() - 1))\n    }\n\n    fn focus_top(&mut self) {\n        self.activate_idx(0);\n    }\n\n    fn focus_bottom(&mut self) {\n        self.activate_idx(self.tiles.len() - 1);\n    }\n\n    fn move_up(&mut self) -> bool {\n        let new_idx = self.active_tile_idx.saturating_sub(1);\n        if self.active_tile_idx == new_idx {\n            return false;\n        }\n\n        let mut ys = self.tile_offsets().skip(self.active_tile_idx);\n        let active_y = ys.next().unwrap().y;\n        let next_y = ys.next().unwrap().y;\n        drop(ys);\n\n        self.tiles.swap(self.active_tile_idx, new_idx);\n        self.data.swap(self.active_tile_idx, new_idx);\n        self.active_tile_idx = new_idx;\n\n        // Animate the movement.\n        let new_active_y = self.tile_offset(new_idx).y;\n        self.tiles[new_idx].animate_move_y_from(active_y - new_active_y);\n        self.tiles[new_idx + 1].animate_move_y_from(active_y - next_y);\n\n        true\n    }\n\n    fn move_down(&mut self) -> bool {\n        let new_idx = min(self.active_tile_idx + 1, self.tiles.len() - 1);\n        if self.active_tile_idx == new_idx {\n            return false;\n        }\n\n        let mut ys = self.tile_offsets().skip(self.active_tile_idx);\n        let active_y = ys.next().unwrap().y;\n        let next_y = ys.next().unwrap().y;\n        drop(ys);\n\n        self.tiles.swap(self.active_tile_idx, new_idx);\n        self.data.swap(self.active_tile_idx, new_idx);\n        self.active_tile_idx = new_idx;\n\n        // Animate the movement.\n        let new_active_y = self.tile_offset(new_idx).y;\n        self.tiles[new_idx].animate_move_y_from(active_y - new_active_y);\n        self.tiles[new_idx - 1].animate_move_y_from(next_y - active_y);\n\n        true\n    }\n\n    fn toggle_width(&mut self, tile_idx: Option<usize>, forwards: bool) {\n        let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);\n\n        let preset_idx = if self.is_full_width || self.is_pending_maximized {\n            None\n        } else {\n            self.preset_width_idx\n        };\n\n        let len = self.options.layout.preset_column_widths.len();\n        let preset_idx = if let Some(idx) = preset_idx {\n            (idx + if forwards { 1 } else { len - 1 }) % len\n        } else {\n            let tile = &self.tiles[tile_idx];\n            let current_window = tile.window_expected_or_current_size().w;\n            let current_tile = tile.tile_expected_or_current_size().w;\n\n            let mut it = self\n                .options\n                .layout\n                .preset_column_widths\n                .iter()\n                .map(|preset| self.resolve_preset_width(*preset));\n\n            if forwards {\n                it.position(|resolved| {\n                    match resolved {\n                        // Some allowance for fractional scaling purposes.\n                        ResolvedSize::Tile(resolved) => current_tile + 1. < resolved,\n                        ResolvedSize::Window(resolved) => current_window + 1. < resolved,\n                    }\n                })\n                .unwrap_or(0)\n            } else {\n                it.rposition(|resolved| {\n                    match resolved {\n                        // Some allowance for fractional scaling purposes.\n                        ResolvedSize::Tile(resolved) => resolved + 1. < current_tile,\n                        ResolvedSize::Window(resolved) => resolved + 1. < current_window,\n                    }\n                })\n                .unwrap_or(len - 1)\n            }\n        };\n\n        let preset = self.options.layout.preset_column_widths[preset_idx];\n        self.set_column_width(SizeChange::from(preset), Some(tile_idx), true);\n\n        self.preset_width_idx = Some(preset_idx);\n    }\n\n    fn toggle_full_width(&mut self) {\n        if self.is_pending_maximized {\n            // Treat it as unmaximize.\n            self.is_pending_maximized = false;\n            self.is_full_width = false;\n        } else {\n            self.is_full_width = !self.is_full_width;\n        }\n\n        self.update_tile_sizes(true);\n    }\n\n    fn set_column_width(&mut self, change: SizeChange, tile_idx: Option<usize>, animate: bool) {\n        let current = if self.is_full_width || self.is_pending_maximized {\n            ColumnWidth::Proportion(1.)\n        } else {\n            self.width\n        };\n\n        let current_px = self.resolve_column_width(current);\n\n        // FIXME: fix overflows then remove limits.\n        const MAX_PX: f64 = 100000.;\n        const MAX_F: f64 = 10000.;\n\n        let width = match (current, change) {\n            (_, SizeChange::SetFixed(fixed)) => {\n                // As a special case, setting a fixed column width will compute it in such a way\n                // that the specified (usually active) window gets that width. This is the\n                // intention behind the ability to set a fixed size.\n                let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);\n                let tile = &self.tiles[tile_idx];\n                ColumnWidth::Fixed(\n                    tile.tile_width_for_window_width(f64::from(fixed))\n                        .clamp(1., MAX_PX),\n                )\n            }\n            (_, SizeChange::SetProportion(proportion)) => {\n                ColumnWidth::Proportion((proportion / 100.).clamp(0., MAX_F))\n            }\n            (_, SizeChange::AdjustFixed(delta)) => {\n                let width = (current_px + f64::from(delta)).clamp(1., MAX_PX);\n                ColumnWidth::Fixed(width)\n            }\n            (ColumnWidth::Proportion(current), SizeChange::AdjustProportion(delta)) => {\n                let proportion = (current + delta / 100.).clamp(0., MAX_F);\n                ColumnWidth::Proportion(proportion)\n            }\n            (ColumnWidth::Fixed(_), SizeChange::AdjustProportion(delta)) => {\n                let full = self.working_area.size.w - self.options.layout.gaps;\n                let current = if full == 0. {\n                    1.\n                } else {\n                    (current_px + self.options.layout.gaps + self.extra_size().w) / full\n                };\n                let proportion = (current + delta / 100.).clamp(0., MAX_F);\n                ColumnWidth::Proportion(proportion)\n            }\n        };\n\n        self.width = width;\n        self.preset_width_idx = None;\n        self.is_full_width = false;\n        self.is_pending_maximized = false;\n        self.update_tile_sizes(animate);\n    }\n\n    fn set_window_height(&mut self, change: SizeChange, tile_idx: Option<usize>, animate: bool) {\n        let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);\n\n        // Start by converting all heights to automatic, since only one window in the column can be\n        // non-auto-height. If the current tile is already non-auto, however, we can skip that\n        // step. Which is not only for optimization, but also preserves automatic weights in case\n        // one window is resized in such a way that other windows hit their min size, and then\n        // back.\n        if matches!(self.data[tile_idx].height, WindowHeight::Auto { .. }) {\n            self.convert_heights_to_auto();\n        }\n\n        let current = self.data[tile_idx].height;\n        let tile = &self.tiles[tile_idx];\n        let current_window_px = match current {\n            WindowHeight::Auto { .. } | WindowHeight::Preset(_) => tile.window_size().h,\n            WindowHeight::Fixed(height) => height,\n        };\n        let current_tile_px = tile.tile_height_for_window_height(current_window_px);\n\n        let working_size = self.working_area.size.h;\n        let gaps = self.options.layout.gaps;\n        let extra_size = self.extra_size().h;\n        let full = working_size - gaps;\n        let current_prop = if full == 0. {\n            1.\n        } else {\n            (current_tile_px + gaps) / full\n        };\n\n        // FIXME: fix overflows then remove limits.\n        const MAX_PX: f64 = 100000.;\n\n        let mut window_height = match change {\n            SizeChange::SetFixed(fixed) => f64::from(fixed),\n            SizeChange::SetProportion(proportion) => {\n                let tile_height = (working_size - gaps) * (proportion / 100.) - gaps - extra_size;\n                tile.window_height_for_tile_height(tile_height)\n            }\n            SizeChange::AdjustFixed(delta) => current_window_px + f64::from(delta),\n            SizeChange::AdjustProportion(delta) => {\n                let proportion = current_prop + delta / 100.;\n                let tile_height = (working_size - gaps) * proportion - gaps - extra_size;\n                tile.window_height_for_tile_height(tile_height)\n            }\n        };\n\n        // Clamp the height according to other windows' min sizes, or simply to working area height.\n        let min_height_taken = if self.display_mode == ColumnDisplay::Tabbed {\n            0.\n        } else {\n            self.tiles\n                .iter()\n                .enumerate()\n                .filter(|(idx, _)| *idx != tile_idx)\n                .map(|(_, tile)| f64::max(1., tile.min_size_nonfullscreen().h) + gaps)\n                .sum::<f64>()\n        };\n        let height_left = working_size - extra_size - gaps - min_height_taken - gaps;\n        let height_left = f64::max(1., tile.window_height_for_tile_height(height_left));\n        window_height = f64::min(height_left, window_height);\n\n        // Clamp it against the window height constraints.\n        let win = &self.tiles[tile_idx].window();\n        let min_h = win.min_size().h;\n        let max_h = win.max_size().h;\n\n        if max_h > 0 {\n            window_height = f64::min(window_height, f64::from(max_h));\n        }\n        if min_h > 0 {\n            window_height = f64::max(window_height, f64::from(min_h));\n        }\n\n        self.data[tile_idx].height = WindowHeight::Fixed(window_height.clamp(1., MAX_PX));\n        self.is_pending_maximized = false;\n        self.update_tile_sizes(animate);\n    }\n\n    fn reset_window_height(&mut self, tile_idx: Option<usize>) {\n        if self.display_mode == ColumnDisplay::Tabbed {\n            // When tabbed, reset window height should work on any window, not just the fixed-size\n            // one.\n            for data in &mut self.data {\n                data.height = WindowHeight::auto_1();\n            }\n        } else {\n            let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);\n            self.data[tile_idx].height = WindowHeight::auto_1();\n        }\n\n        self.update_tile_sizes(true);\n    }\n\n    fn toggle_window_height(&mut self, tile_idx: Option<usize>, forwards: bool) {\n        let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);\n\n        // Start by converting all heights to automatic, since only one window in the column can be\n        // non-auto-height. If the current tile is already non-auto, however, we can skip that\n        // step. Which is not only for optimization, but also preserves automatic weights in case\n        // one window is resized in such a way that other windows hit their min size, and then\n        // back.\n        if matches!(self.data[tile_idx].height, WindowHeight::Auto { .. }) {\n            self.convert_heights_to_auto();\n        }\n\n        let len = self.options.layout.preset_window_heights.len();\n        let preset_idx = match self.data[tile_idx].height {\n            WindowHeight::Preset(idx) if !self.is_pending_maximized => {\n                (idx + if forwards { 1 } else { len - 1 }) % len\n            }\n            _ => {\n                let current = self.data[tile_idx].size.h;\n                let tile = &self.tiles[tile_idx];\n\n                let mut it = self\n                    .options\n                    .layout\n                    .preset_window_heights\n                    .iter()\n                    .copied()\n                    .map(|preset| {\n                        let window_height = match self.resolve_preset_height(preset) {\n                            ResolvedSize::Tile(h) => tile.window_height_for_tile_height(h),\n                            ResolvedSize::Window(h) => h,\n                        };\n                        tile.tile_height_for_window_height(window_height.round().clamp(1., 100000.))\n                    });\n\n                if forwards {\n                    it.position(|resolved| {\n                        // Some allowance for fractional scaling purposes.\n                        current + 1. < resolved\n                    })\n                    .unwrap_or(0)\n                } else {\n                    it.rposition(|resolved| {\n                        // Some allowance for fractional scaling purposes.\n                        resolved + 1. < current\n                    })\n                    .unwrap_or(len - 1)\n                }\n            }\n        };\n        self.data[tile_idx].height = WindowHeight::Preset(preset_idx);\n        self.is_pending_maximized = false;\n        self.update_tile_sizes(true);\n    }\n\n    /// Converts all heights in the column to automatic, preserving the apparent heights.\n    ///\n    /// All weights are recomputed to preserve the current tile heights while \"centering\" the\n    /// weights at the median window height (it gets weight = 1).\n    ///\n    /// One case where apparent heights will not be preserved is when the column is taller than the\n    /// working area.\n    fn convert_heights_to_auto(&mut self) {\n        let heights: Vec<_> = self.tiles.iter().map(|tile| tile.tile_size().h).collect();\n\n        // Weights are invariant to multiplication: a column with weights 2, 2, 1 is equivalent to\n        // a column with weights 4, 4, 2. So we find the median window height and use that as 1.\n        let mut sorted = heights.clone();\n        sorted.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());\n        let median = sorted[sorted.len() / 2];\n\n        for (data, height) in zip(&mut self.data, heights) {\n            let weight = height / median;\n            data.height = WindowHeight::Auto { weight };\n        }\n    }\n\n    fn set_fullscreen(&mut self, is_fullscreen: bool) {\n        if self.is_pending_fullscreen == is_fullscreen {\n            return;\n        }\n\n        if is_fullscreen {\n            assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed);\n        }\n\n        self.is_pending_fullscreen = is_fullscreen;\n        self.update_tile_sizes(true);\n    }\n\n    fn set_maximized(&mut self, maximize: bool) {\n        if self.is_pending_maximized == maximize {\n            return;\n        }\n\n        if maximize {\n            assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed);\n        }\n\n        self.is_pending_maximized = maximize;\n        self.update_tile_sizes(true);\n    }\n\n    fn set_column_display(&mut self, display: ColumnDisplay) {\n        if self.display_mode == display {\n            return;\n        }\n\n        // Animate the movement.\n        //\n        // We're doing some shortcuts here because we know that currently normal vs. tabbed can\n        // only cause a vertical shift + a shift to the origin.\n        //\n        // Doing it this way to avoid storing all tile positions in a vector. If more display modes\n        // are added it might be simpler to just collect everything into a smallvec.\n        let prev_origin = self.tiles_origin();\n        self.display_mode = display;\n        let new_origin = self.tiles_origin();\n        let origin_delta = prev_origin - new_origin;\n\n        // When need to walk the tiles in the normal display mode to get the right offsets.\n        self.display_mode = ColumnDisplay::Normal;\n        for (tile, pos) in self.tiles_mut() {\n            let mut y_delta = pos.y - prev_origin.y;\n\n            // Invert the Y motion when transitioning *to* normal display mode.\n            if display == ColumnDisplay::Normal {\n                y_delta *= -1.;\n            }\n\n            let mut delta = origin_delta;\n            delta.y += y_delta;\n            tile.animate_move_from(delta);\n        }\n\n        // Animate the opacity.\n        for (idx, tile) in self.tiles.iter_mut().enumerate() {\n            let is_active = idx == self.active_tile_idx;\n            if !is_active {\n                let (from, to) = if display == ColumnDisplay::Tabbed {\n                    (1., 0.)\n                } else {\n                    (0., 1.)\n                };\n                tile.animate_alpha(from, to, self.options.animations.window_movement.0);\n            }\n        }\n\n        // Animate the appearance of the tab indicator.\n        if display == ColumnDisplay::Tabbed {\n            self.tab_indicator.start_open_animation(\n                self.clock.clone(),\n                self.options.animations.window_movement.0,\n            );\n        }\n\n        // Now switch the display mode for real.\n        self.display_mode = display;\n        self.update_tile_sizes(true);\n    }\n\n    fn tiles_origin(&self) -> Point<f64, Logical> {\n        let mut origin = Point::from((0., 0.));\n\n        match self.sizing_mode() {\n            SizingMode::Normal => (),\n            SizingMode::Maximized => {\n                origin.y += self.parent_area.loc.y;\n                return origin;\n            }\n            SizingMode::Fullscreen => return origin,\n        }\n\n        origin.y += self.working_area.loc.y + self.options.layout.gaps;\n\n        if self.display_mode == ColumnDisplay::Tabbed {\n            origin += self\n                .tab_indicator\n                .content_offset(self.tiles.len(), self.scale);\n        }\n\n        origin\n    }\n\n    // HACK: pass a self.data iterator in manually as a workaround for the lack of method partial\n    // borrowing. Note that this method's return value does not borrow the entire &Self!\n    fn tile_offsets_iter(\n        &self,\n        data: impl Iterator<Item = TileData>,\n    ) -> impl Iterator<Item = Point<f64, Logical>> {\n        // FIXME: this should take into account always-center-single-column, which means that\n        // Column should somehow know when it is being centered due to being the single column on\n        // the workspace or some other reason.\n        let center = self.options.layout.center_focused_column == CenterFocusedColumn::Always;\n        let gaps = self.options.layout.gaps;\n        let tabbed = self.display_mode == ColumnDisplay::Tabbed;\n\n        // Does not include extra size from the tab indicator.\n        let tiles_width = self\n            .data\n            .iter()\n            .map(|data| NotNan::new(data.size.w).unwrap())\n            .max()\n            .map(NotNan::into_inner)\n            .unwrap_or(0.);\n\n        let mut origin = self.tiles_origin();\n\n        // Chain with a dummy value to be able to get one past all tiles' Y.\n        let dummy = TileData {\n            height: WindowHeight::auto_1(),\n            size: Size::default(),\n            interactively_resizing_by_left_edge: false,\n        };\n        let data = data.chain(iter::once(dummy));\n\n        data.map(move |data| {\n            let mut pos = origin;\n\n            if center {\n                pos.x += (tiles_width - data.size.w) / 2.;\n            } else if data.interactively_resizing_by_left_edge {\n                pos.x += tiles_width - data.size.w;\n            }\n\n            if !tabbed {\n                origin.y += data.size.h + gaps;\n            }\n\n            pos\n        })\n    }\n\n    fn tile_offsets(&self) -> impl Iterator<Item = Point<f64, Logical>> + '_ {\n        self.tile_offsets_iter(self.data.iter().copied())\n    }\n\n    fn tile_offset(&self, tile_idx: usize) -> Point<f64, Logical> {\n        self.tile_offsets().nth(tile_idx).unwrap()\n    }\n\n    fn tile_offsets_in_render_order(\n        &self,\n        data: impl Iterator<Item = TileData>,\n    ) -> impl Iterator<Item = Point<f64, Logical>> {\n        let active_idx = self.active_tile_idx;\n        let active_pos = self.tile_offset(active_idx);\n        let offsets = self\n            .tile_offsets_iter(data)\n            .enumerate()\n            .filter_map(move |(idx, pos)| (idx != active_idx).then_some(pos));\n        iter::once(active_pos).chain(offsets)\n    }\n\n    pub fn tiles(&self) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>)> + '_ {\n        let offsets = self.tile_offsets_iter(self.data.iter().copied());\n        zip(&self.tiles, offsets)\n    }\n\n    fn tiles_mut(&mut self) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> + '_ {\n        let offsets = self.tile_offsets_iter(self.data.iter().copied());\n        zip(&mut self.tiles, offsets)\n    }\n\n    fn tiles_in_render_order(\n        &self,\n    ) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>, bool)> + '_ {\n        let offsets = self.tile_offsets_in_render_order(self.data.iter().copied());\n\n        let (first, rest) = self.tiles.split_at(self.active_tile_idx);\n        let (active, rest) = rest.split_at(1);\n\n        let active = active.iter().map(|tile| (tile, true));\n\n        let rest_visible = self.display_mode != ColumnDisplay::Tabbed;\n        let rest = first.iter().chain(rest);\n        let rest = rest.map(move |tile| (tile, rest_visible));\n\n        let tiles = active.chain(rest);\n        zip(tiles, offsets).map(|((tile, visible), pos)| (tile, pos, visible))\n    }\n\n    fn tiles_in_render_order_mut(\n        &mut self,\n    ) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> + '_ {\n        let offsets = self.tile_offsets_in_render_order(self.data.iter().copied());\n\n        let (first, rest) = self.tiles.split_at_mut(self.active_tile_idx);\n        let (active, rest) = rest.split_at_mut(1);\n\n        let tiles = active.iter_mut().chain(first).chain(rest);\n        zip(tiles, offsets)\n    }\n\n    fn tab_indicator_area(&self) -> Rectangle<f64, Logical> {\n        // We'd like to use the active tile's animated size for the tab indicator, however we need\n        // to be mindful of the case where the active tile is smaller than some other tile in the\n        // column. The column assumes the size of the largest tile.\n        //\n        // We expect users to mainly resize tabbed columns by width, so matching the animated size\n        // is more important here. Besides, we always try to resize all windows in a column to the\n        // same width when possible, and also the animation for going into tabbed mode doesn't move\n        // tiles horizontally as much.\n        //\n        // For height though, it's a different story. First, users probably aren't resizing a\n        // tabbed column by height. Second, we don't match windows by height, so it's easy to have\n        // a smaller active tile than the rest of the column, e.g. by adding a fixed-size dialog.\n        // Then, switching to that dialog and back should ideally keep the tab indicator position\n        // fixed. Third, the animation for making a column tabbed moves tiles vertically, and using\n        // the active tile's animated size in this case only works for the topmost tile, and looks\n        // broken otherwise.\n        let mut max_height = 0.;\n        for tile in &self.tiles {\n            max_height = f64::max(max_height, tile.tile_size().h);\n        }\n\n        let tile = &self.tiles[self.active_tile_idx];\n        let area_size = Size::from((tile.animated_tile_size().w, max_height));\n\n        Rectangle::new(self.tiles_origin(), area_size)\n    }\n\n    pub fn start_open_animation(&mut self, id: &W::Id) -> bool {\n        for tile in &mut self.tiles {\n            if tile.window().id() == id {\n                tile.start_open_animation();\n\n                // Animate the appearance of the tab indicator.\n                if self.display_mode == ColumnDisplay::Tabbed\n                    && self.sizing_mode().is_normal()\n                    && self.tiles.len() == 1\n                    && !self.tab_indicator.config().hide_when_single_tab\n                {\n                    self.tab_indicator.start_open_animation(\n                        self.clock.clone(),\n                        self.options.animations.window_open.anim,\n                    );\n                }\n\n                return true;\n            }\n        }\n\n        false\n    }\n\n    #[cfg(test)]\n    fn verify_invariants(&self) {\n        assert!(!self.tiles.is_empty(), \"columns can't be empty\");\n        assert!(self.active_tile_idx < self.tiles.len());\n        assert_eq!(self.tiles.len(), self.data.len());\n\n        if !self.pending_sizing_mode().is_normal() {\n            assert!(self.tiles.len() == 1 || self.display_mode == ColumnDisplay::Tabbed);\n        }\n\n        if let Some(idx) = self.preset_width_idx {\n            assert!(idx < self.options.layout.preset_column_widths.len());\n        }\n\n        let is_tabbed = self.display_mode == ColumnDisplay::Tabbed;\n\n        let tile_count = self.tiles.len();\n        if tile_count == 1 {\n            if let WindowHeight::Auto { weight } = self.data[0].height {\n                assert_eq!(\n                    weight, 1.,\n                    \"auto height weight must reset to 1 for a single window\"\n                );\n            }\n        }\n\n        let working_size = self.working_area.size;\n        let extra_size = self.extra_size();\n        let gaps = self.options.layout.gaps;\n\n        let mut found_fixed = false;\n        let mut total_height = 0.;\n        let mut total_min_height = 0.;\n        for (tile, data) in zip(&self.tiles, &self.data) {\n            assert!(Rc::ptr_eq(&self.options, &tile.options));\n            assert_eq!(self.clock, tile.clock);\n            assert_eq!(self.scale, tile.scale());\n            assert_eq!(\n                self.pending_sizing_mode(),\n                tile.window().pending_sizing_mode()\n            );\n            assert_eq!(self.view_size, tile.view_size());\n            tile.verify_invariants();\n\n            let mut data2 = *data;\n            data2.update(tile);\n            assert_eq!(data, &data2, \"tile data must be up to date\");\n\n            if matches!(data.height, WindowHeight::Fixed(_)) {\n                assert!(\n                    !found_fixed,\n                    \"there can only be one fixed-height window in a column\"\n                );\n                found_fixed = true;\n            }\n\n            if let WindowHeight::Preset(idx) = data.height {\n                assert!(self.options.layout.preset_window_heights.len() > idx);\n            }\n\n            let requested_size = tile.window().requested_size().unwrap();\n            let requested_tile_height =\n                tile.tile_height_for_window_height(f64::from(requested_size.h));\n            let min_tile_height = f64::max(1., tile.min_size_nonfullscreen().h);\n\n            if !is_tabbed\n                && self.pending_sizing_mode().is_normal()\n                && self.scale.round() == self.scale\n                && working_size.h.round() == working_size.h\n                && gaps.round() == gaps\n            {\n                let total_height = requested_tile_height + gaps * 2. + extra_size.h;\n                let total_min_height = min_tile_height + gaps * 2. + extra_size.h;\n                let max_height = f64::max(total_min_height, working_size.h);\n                assert!(\n                    total_height <= max_height,\n                    \"each tile in a column mustn't go beyond working area height \\\n                     (tile height {total_height} > max height {max_height})\"\n                );\n            }\n\n            total_height += requested_tile_height;\n            total_min_height += min_tile_height;\n        }\n\n        if !is_tabbed\n            && tile_count > 1\n            && self.scale.round() == self.scale\n            && working_size.h.round() == working_size.h\n            && gaps.round() == gaps\n        {\n            total_height += gaps * (tile_count + 1) as f64 + extra_size.h;\n            total_min_height += gaps * (tile_count + 1) as f64 + extra_size.h;\n            let max_height = f64::max(total_min_height, working_size.h);\n            assert!(\n                total_height <= max_height,\n                \"multiple tiles in a column mustn't go beyond working area height \\\n                 (total height {total_height} > max height {max_height})\"\n            );\n        }\n    }\n}\n\nfn compute_new_view_offset(\n    cur_x: f64,\n    view_width: f64,\n    new_col_x: f64,\n    new_col_width: f64,\n    gaps: f64,\n) -> f64 {\n    // If the column is wider than the view, always left-align it.\n    if view_width <= new_col_width {\n        return 0.;\n    }\n\n    // Compute the padding in case it needs to be smaller due to large tile width.\n    let padding = ((view_width - new_col_width) / 2.).clamp(0., gaps);\n\n    // Compute the desired new X with padding.\n    let new_x = new_col_x - padding;\n    let new_right_x = new_col_x + new_col_width + padding;\n\n    // If the column is already fully visible, leave the view as is.\n    if cur_x <= new_x && new_right_x <= cur_x + view_width {\n        return -(new_col_x - cur_x);\n    }\n\n    // Otherwise, prefer the alignment that results in less motion from the current position.\n    let dist_to_left = (cur_x - new_x).abs();\n    let dist_to_right = ((cur_x + view_width) - new_right_x).abs();\n    if dist_to_left <= dist_to_right {\n        -padding\n    } else {\n        -(view_width - padding - new_col_width)\n    }\n}\n\nfn compute_working_area(\n    parent_area: Rectangle<f64, Logical>,\n    scale: f64,\n    struts: Struts,\n) -> Rectangle<f64, Logical> {\n    let mut working_area = parent_area;\n\n    // Add struts.\n    working_area.size.w = f64::max(0., working_area.size.w - struts.left.0 - struts.right.0);\n    working_area.loc.x += struts.left.0;\n\n    working_area.size.h = f64::max(0., working_area.size.h - struts.top.0 - struts.bottom.0);\n    working_area.loc.y += struts.top.0;\n\n    // Round location to start at a physical pixel.\n    let loc = working_area\n        .loc\n        .to_physical_precise_ceil(scale)\n        .to_logical(scale);\n\n    let mut size_diff = (loc - working_area.loc).to_size();\n    size_diff.w = f64::min(working_area.size.w, size_diff.w);\n    size_diff.h = f64::min(working_area.size.h, size_diff.h);\n\n    working_area.size -= size_diff;\n    working_area.loc = loc;\n\n    working_area\n}\n\nfn compute_toplevel_bounds(\n    border_config: niri_config::Border,\n    working_area_size: Size<f64, Logical>,\n    extra_size: Size<f64, Logical>,\n    gaps: f64,\n) -> Size<i32, Logical> {\n    let mut border = 0.;\n    if !border_config.off {\n        border = border_config.width * 2.;\n    }\n\n    Size::from((\n        f64::max(working_area_size.w - gaps * 2. - extra_size.w - border, 1.),\n        f64::max(working_area_size.h - gaps * 2. - extra_size.h - border, 1.),\n    ))\n    .to_i32_floor()\n}\n\nfn cancel_resize_for_column<W: LayoutElement>(\n    interactive_resize: &mut Option<InteractiveResize<W>>,\n    column: &mut Column<W>,\n) {\n    if let Some(resize) = interactive_resize {\n        if column.contains(&resize.window) {\n            *interactive_resize = None;\n        }\n    }\n\n    for tile in &mut column.tiles {\n        tile.window_mut().cancel_interactive_resize();\n    }\n}\n\nfn resolve_preset_size(\n    preset: PresetSize,\n    options: &Options,\n    view_size: f64,\n    extra_size: f64,\n) -> ResolvedSize {\n    match preset {\n        PresetSize::Proportion(proportion) => ResolvedSize::Tile(\n            (view_size - options.layout.gaps) * proportion - options.layout.gaps - extra_size,\n        ),\n        PresetSize::Fixed(width) => ResolvedSize::Window(f64::from(width)),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use niri_config::FloatOrInt;\n\n    use super::*;\n    use crate::utils::round_logical_in_physical;\n\n    #[test]\n    fn working_area_starts_at_physical_pixel() {\n        let struts = Struts {\n            left: FloatOrInt(0.5),\n            right: FloatOrInt(1.),\n            top: FloatOrInt(0.75),\n            bottom: FloatOrInt(1.),\n        };\n\n        let parent_area = Rectangle::from_size(Size::from((1280., 720.)));\n        let area = compute_working_area(parent_area, 1., struts);\n\n        assert_eq!(round_logical_in_physical(1., area.loc.x), area.loc.x);\n        assert_eq!(round_logical_in_physical(1., area.loc.y), area.loc.y);\n    }\n\n    #[test]\n    fn large_fractional_strut() {\n        let struts = Struts {\n            left: FloatOrInt(0.),\n            right: FloatOrInt(0.),\n            top: FloatOrInt(50000.5),\n            bottom: FloatOrInt(0.),\n        };\n\n        let parent_area = Rectangle::from_size(Size::from((1280., 720.)));\n        compute_working_area(parent_area, 1., struts);\n    }\n}\n"
  },
  {
    "path": "src/layout/shadow.rs",
    "content": "use std::iter::zip;\n\nuse niri_config::CornerRadius;\nuse smithay::utils::{Logical, Point, Rectangle, Size};\n\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::shadow::ShadowRenderElement;\n\n#[derive(Debug)]\npub struct Shadow {\n    shader_rects: Vec<Rectangle<f64, Logical>>,\n    shaders: Vec<ShadowRenderElement>,\n    config: niri_config::Shadow,\n}\n\nimpl Shadow {\n    pub fn new(config: niri_config::Shadow) -> Self {\n        Self {\n            shader_rects: Vec::new(),\n            shaders: Vec::new(),\n            config,\n        }\n    }\n\n    pub fn update_config(&mut self, config: niri_config::Shadow) {\n        self.config = config;\n    }\n\n    pub fn update_shaders(&mut self) {\n        for elem in &mut self.shaders {\n            elem.damage_all();\n        }\n    }\n\n    pub fn update_render_elements(\n        &mut self,\n        win_size: Size<f64, Logical>,\n        is_active: bool,\n        radius: CornerRadius,\n        scale: f64,\n        alpha: f32,\n    ) {\n        let ceil = |logical: f64| (logical * scale).ceil() / scale;\n\n        // All of this stuff should end up aligned to physical pixels because:\n        // * Window size is rounded to physical pixels before being passed to this function.\n        // * We will ceil the corner radii below.\n        // * We do not divide anything, only add, subtract and multiply by integers.\n        // * At rendering time, tile positions are rounded to physical pixels.\n\n        let width = self.config.softness;\n        // Like in CSS box-shadow.\n        let sigma = width / 2.;\n        // Adjust width to draw all necessary pixels.\n        let width = ceil(sigma * 3.);\n\n        let offset = self.config.offset;\n        let offset = Point::from((ceil(offset.x.0), ceil(offset.y.0)));\n\n        let spread = self.config.spread;\n        let spread = ceil(spread.abs()).copysign(spread);\n        let offset = offset - Point::from((spread, spread));\n\n        let win_radius = radius.fit_to(win_size.w as f32, win_size.h as f32);\n\n        let box_size = if spread >= 0. {\n            win_size + Size::from((spread, spread)).upscale(2.)\n        } else {\n            // This is a saturating sub.\n            win_size - Size::from((-spread, -spread)).upscale(2.)\n        };\n        let radius = win_radius.expanded_by(spread as f32);\n\n        let shader_size = box_size + Size::from((width, width)).upscale(2.);\n\n        let color = if is_active {\n            self.config.color\n        } else {\n            // Default to slightly more transparent.\n            self.config\n                .inactive_color\n                .unwrap_or(self.config.color * 0.75)\n        };\n\n        let shader_geo = Rectangle::new(Point::from((-width, -width)), shader_size);\n\n        // This is actually offset relative to shader_geo, this is handled below.\n        let window_geo = Rectangle::new(Point::from((0., 0.)), win_size);\n\n        if !self.config.draw_behind_window {\n            let top_left = ceil(f64::from(win_radius.top_left));\n            let top_right = f64::min(win_size.w - top_left, ceil(f64::from(win_radius.top_right)));\n            let bottom_left = f64::min(\n                win_size.h - top_left,\n                ceil(f64::from(win_radius.bottom_left)),\n            );\n            let bottom_right = f64::min(\n                win_size.h - top_right,\n                f64::min(\n                    win_size.w - bottom_left,\n                    ceil(f64::from(win_radius.bottom_right)),\n                ),\n            );\n\n            let top_left = Rectangle::new(Point::from((0., 0.)), Size::from((top_left, top_left)));\n            let top_right = Rectangle::new(\n                Point::from((win_size.w - top_right, 0.)),\n                Size::from((top_right, top_right)),\n            );\n            let bottom_right = Rectangle::new(\n                Point::from((win_size.w - bottom_right, win_size.h - bottom_right)),\n                Size::from((bottom_right, bottom_right)),\n            );\n            let bottom_left = Rectangle::new(\n                Point::from((0., win_size.h - bottom_left)),\n                Size::from((bottom_left, bottom_left)),\n            );\n\n            let mut background =\n                window_geo.subtract_rects([top_left, top_right, bottom_right, bottom_left]);\n            for rect in &mut background {\n                rect.loc -= offset;\n            }\n\n            self.shader_rects = shader_geo.subtract_rects(background);\n            self.shaders\n                .resize_with(self.shader_rects.len(), Default::default);\n\n            for (shader, rect) in zip(&mut self.shaders, &mut self.shader_rects) {\n                shader.update(\n                    rect.size,\n                    Rectangle::new(rect.loc.upscale(-1.), box_size),\n                    color,\n                    sigma as f32,\n                    radius,\n                    scale as f32,\n                    Rectangle::new(window_geo.loc - offset - rect.loc, window_geo.size),\n                    win_radius,\n                    alpha,\n                );\n\n                rect.loc += offset;\n            }\n        } else {\n            self.shader_rects.resize_with(1, Default::default);\n            self.shader_rects[0] = shader_geo;\n\n            self.shaders.resize_with(1, Default::default);\n            self.shaders[0].update(\n                shader_geo.size,\n                Rectangle::new(shader_geo.loc.upscale(-1.), box_size),\n                color,\n                sigma as f32,\n                radius,\n                scale as f32,\n                Rectangle::zero(),\n                Default::default(),\n                alpha,\n            );\n\n            self.shader_rects[0].loc += offset;\n        }\n    }\n\n    pub fn render(\n        &self,\n        renderer: &mut impl NiriRenderer,\n        location: Point<f64, Logical>,\n        push: &mut dyn FnMut(ShadowRenderElement),\n    ) {\n        if !self.config.on {\n            return;\n        }\n\n        let has_shadow_shader = ShadowRenderElement::has_shader(renderer);\n        if !has_shadow_shader {\n            return;\n        }\n\n        for (shader, rect) in zip(&self.shaders, &self.shader_rects) {\n            push(shader.clone().with_location(location + rect.loc));\n        }\n    }\n}\n"
  },
  {
    "path": "src/layout/tab_indicator.rs",
    "content": "use std::iter::zip;\nuse std::mem;\n\nuse niri_config::{CornerRadius, Gradient, GradientRelativeTo, TabIndicatorPosition};\nuse smithay::utils::{Logical, Point, Rectangle, Size};\n\nuse super::tile::Tile;\nuse super::LayoutElement;\nuse crate::animation::{Animation, Clock};\nuse crate::niri_render_elements;\nuse crate::render_helpers::border::BorderRenderElement;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::utils::{\n    floor_logical_in_physical_max1, round_logical_in_physical, round_logical_in_physical_max1,\n};\n\n#[derive(Debug)]\npub struct TabIndicator {\n    shader_locs: Vec<Point<f64, Logical>>,\n    shaders: Vec<BorderRenderElement>,\n    open_anim: Option<Animation>,\n    config: niri_config::TabIndicator,\n}\n\n#[derive(Debug)]\npub struct TabInfo {\n    /// Gradient for the tab indicator.\n    pub gradient: Gradient,\n    /// Tab geometry in the same coordinate system as the area.\n    pub geometry: Rectangle<f64, Logical>,\n}\n\nniri_render_elements! {\n    TabIndicatorRenderElement => {\n        Gradient = BorderRenderElement,\n    }\n}\n\nimpl TabIndicator {\n    pub fn new(config: niri_config::TabIndicator) -> Self {\n        Self {\n            shader_locs: Vec::new(),\n            shaders: Vec::new(),\n            open_anim: None,\n            config,\n        }\n    }\n\n    pub fn update_config(&mut self, config: niri_config::TabIndicator) {\n        self.config = config;\n    }\n\n    pub fn update_shaders(&mut self) {\n        for elem in &mut self.shaders {\n            elem.damage_all();\n        }\n    }\n\n    pub fn advance_animations(&mut self) {\n        if let Some(anim) = &mut self.open_anim {\n            if anim.is_done() {\n                self.open_anim = None;\n            }\n        }\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        self.open_anim.is_some()\n    }\n\n    pub fn start_open_animation(&mut self, clock: Clock, config: niri_config::Animation) {\n        self.open_anim = Some(Animation::new(clock, 0., 1., 0., config));\n    }\n\n    fn tab_rects(\n        &self,\n        area: Rectangle<f64, Logical>,\n        count: usize,\n        scale: f64,\n    ) -> impl Iterator<Item = Rectangle<f64, Logical>> {\n        let round = |logical: f64| round_logical_in_physical(scale, logical);\n        let round_max1 = |logical: f64| round_logical_in_physical_max1(scale, logical);\n\n        let progress = self.open_anim.as_ref().map_or(1., |a| a.value().max(0.));\n\n        let width = round_max1(self.config.width);\n        let gap = self.config.gap;\n        let gap = round_max1(gap.abs()).copysign(gap);\n        let gaps_between = round_max1(self.config.gaps_between_tabs);\n\n        let position = self.config.position;\n        let side = match position {\n            TabIndicatorPosition::Left | TabIndicatorPosition::Right => area.size.h,\n            TabIndicatorPosition::Top | TabIndicatorPosition::Bottom => area.size.w,\n        };\n        let total_prop = self.config.length.total_proportion.unwrap_or(0.5);\n        let min_length = round(side * total_prop.clamp(0., 2.));\n\n        // Compute px_per_tab before applying the animation to gaps_between in order to avoid it\n        // growing and shrinking over the duration of the animation.\n        let pixel = 1. / scale;\n        let shortest_length = count as f64 * (pixel + gaps_between) - gaps_between;\n        let length = f64::max(min_length, shortest_length);\n        let px_per_tab = (length + gaps_between) / count as f64 - gaps_between;\n\n        let px_per_tab = px_per_tab * progress;\n        let gaps_between = round(self.config.gaps_between_tabs * progress);\n\n        let length = count as f64 * (px_per_tab + gaps_between) - gaps_between;\n        let px_per_tab = floor_logical_in_physical_max1(scale, px_per_tab);\n        let floored_length = count as f64 * (px_per_tab + gaps_between) - gaps_between;\n        let mut ones_left = ((length - floored_length) / pixel).round() as usize;\n\n        let mut shader_loc = Point::from((-gap - width, round((side - length) / 2.)));\n        match position {\n            TabIndicatorPosition::Left => (),\n            TabIndicatorPosition::Right => shader_loc.x = area.size.w + gap,\n            TabIndicatorPosition::Top => mem::swap(&mut shader_loc.x, &mut shader_loc.y),\n            TabIndicatorPosition::Bottom => {\n                shader_loc.x = shader_loc.y;\n                shader_loc.y = area.size.h + gap;\n            }\n        }\n        shader_loc += area.loc;\n\n        (0..count).map(move |_| {\n            let mut px_per_tab = px_per_tab;\n            if ones_left > 0 {\n                ones_left -= 1;\n                px_per_tab += pixel;\n            }\n\n            let loc = shader_loc;\n\n            match position {\n                TabIndicatorPosition::Left | TabIndicatorPosition::Right => {\n                    shader_loc.y += px_per_tab + gaps_between\n                }\n                TabIndicatorPosition::Top | TabIndicatorPosition::Bottom => {\n                    shader_loc.x += px_per_tab + gaps_between\n                }\n            }\n\n            let size = match position {\n                TabIndicatorPosition::Left | TabIndicatorPosition::Right => {\n                    Size::from((width, px_per_tab))\n                }\n                TabIndicatorPosition::Top | TabIndicatorPosition::Bottom => {\n                    Size::from((px_per_tab, width))\n                }\n            };\n\n            Rectangle::new(loc, size)\n        })\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn update_render_elements(\n        &mut self,\n        enabled: bool,\n        // Geometry of the tabs area.\n        area: Rectangle<f64, Logical>,\n        // View rect relative to the tabs area.\n        area_view_rect: Rectangle<f64, Logical>,\n        // Tab count, should match the tabs iterator length.\n        tab_count: usize,\n        tabs: impl Iterator<Item = TabInfo>,\n        is_active: bool,\n        scale: f64,\n    ) {\n        if !enabled || self.config.off {\n            self.shader_locs.clear();\n            self.shaders.clear();\n            return;\n        }\n\n        let count = tab_count;\n        if self.config.hide_when_single_tab && count == 1 {\n            self.shader_locs.clear();\n            self.shaders.clear();\n            return;\n        }\n\n        self.shaders.resize_with(count, Default::default);\n        self.shader_locs.resize_with(count, Default::default);\n\n        let position = self.config.position;\n        let radius = self.config.corner_radius as f32;\n        let shared_rounded_corners = self.config.gaps_between_tabs == 0.;\n        let mut tabs_left = tab_count;\n\n        let rects = self.tab_rects(area, count, scale);\n        for ((shader, loc), (tab, rect)) in zip(\n            zip(&mut self.shaders, &mut self.shader_locs),\n            zip(tabs, rects),\n        ) {\n            *loc = rect.loc;\n\n            let mut gradient_area = match tab.gradient.relative_to {\n                GradientRelativeTo::Window => tab.geometry,\n                GradientRelativeTo::WorkspaceView => area_view_rect,\n            };\n            gradient_area.loc -= *loc;\n\n            let mut color_from = tab.gradient.from;\n            let mut color_to = tab.gradient.to;\n            if !is_active {\n                color_from *= 0.5;\n                color_to *= 0.5;\n            }\n\n            let radius = if shared_rounded_corners && tab_count > 1 {\n                if tabs_left == tab_count {\n                    // First tab.\n                    match position {\n                        TabIndicatorPosition::Left | TabIndicatorPosition::Right => CornerRadius {\n                            top_left: radius,\n                            top_right: radius,\n                            bottom_right: 0.,\n                            bottom_left: 0.,\n                        },\n                        TabIndicatorPosition::Top | TabIndicatorPosition::Bottom => CornerRadius {\n                            top_left: radius,\n                            top_right: 0.,\n                            bottom_right: 0.,\n                            bottom_left: radius,\n                        },\n                    }\n                } else if tabs_left == 1 {\n                    // Last tab.\n                    match position {\n                        TabIndicatorPosition::Left | TabIndicatorPosition::Right => CornerRadius {\n                            top_left: 0.,\n                            top_right: 0.,\n                            bottom_right: radius,\n                            bottom_left: radius,\n                        },\n                        TabIndicatorPosition::Top | TabIndicatorPosition::Bottom => CornerRadius {\n                            top_left: 0.,\n                            top_right: radius,\n                            bottom_right: radius,\n                            bottom_left: 0.,\n                        },\n                    }\n                } else {\n                    // Tab in the middle.\n                    CornerRadius::default()\n                }\n            } else {\n                // Separate tabs, or the only tab.\n                CornerRadius::from(radius)\n            };\n            let radius = radius.fit_to(rect.size.w as f32, rect.size.h as f32);\n            tabs_left -= 1;\n\n            shader.update(\n                rect.size,\n                gradient_area,\n                tab.gradient.in_,\n                color_from,\n                color_to,\n                ((tab.gradient.angle as f32) - 90.).to_radians(),\n                Rectangle::from_size(rect.size),\n                0.,\n                radius,\n                scale as f32,\n                1.,\n            );\n        }\n    }\n\n    pub fn hit(\n        &self,\n        area: Rectangle<f64, Logical>,\n        tab_count: usize,\n        scale: f64,\n        point: Point<f64, Logical>,\n    ) -> Option<usize> {\n        if self.config.off {\n            return None;\n        }\n\n        let count = tab_count;\n        if self.config.hide_when_single_tab && count == 1 {\n            return None;\n        }\n\n        self.tab_rects(area, count, scale)\n            .enumerate()\n            .find_map(|(idx, rect)| rect.contains(point).then_some(idx))\n    }\n\n    pub fn render(\n        &self,\n        renderer: &mut impl NiriRenderer,\n        pos: Point<f64, Logical>,\n        push: &mut dyn FnMut(TabIndicatorRenderElement),\n    ) {\n        let has_border_shader = BorderRenderElement::has_shader(renderer);\n        if !has_border_shader {\n            return;\n        }\n\n        for (shader, loc) in zip(&self.shaders, &self.shader_locs) {\n            let elem = shader.clone().with_location(pos + *loc);\n            push(TabIndicatorRenderElement::from(elem));\n        }\n    }\n\n    /// Extra size occupied by the tab indicator.\n    pub fn extra_size(&self, tab_count: usize, scale: f64) -> Size<f64, Logical> {\n        if self.config.off\n            || !self.config.place_within_column\n            || (self.config.hide_when_single_tab && tab_count == 1)\n        {\n            return Size::from((0., 0.));\n        }\n\n        let round = |logical: f64| round_logical_in_physical(scale, logical);\n        let width = round(self.config.width);\n        let gap = round(self.config.gap);\n\n        // No, I am *not* falling into the rabbit hole of \"what if the tab indicator is wide enough\n        // that it peeks from the other side of the window\".\n        let size = f64::max(0., width + gap);\n\n        match self.config.position {\n            TabIndicatorPosition::Left | TabIndicatorPosition::Right => Size::from((size, 0.)),\n            TabIndicatorPosition::Top | TabIndicatorPosition::Bottom => Size::from((0., size)),\n        }\n    }\n\n    /// Offset of the tabbed content due to space occupied by the tab indicator.\n    pub fn content_offset(&self, tab_count: usize, scale: f64) -> Point<f64, Logical> {\n        match self.config.position {\n            TabIndicatorPosition::Left | TabIndicatorPosition::Top => {\n                self.extra_size(tab_count, scale).to_point()\n            }\n            TabIndicatorPosition::Right | TabIndicatorPosition::Bottom => Point::from((0., 0.)),\n        }\n    }\n\n    pub fn config(&self) -> niri_config::TabIndicator {\n        self.config\n    }\n}\n\nimpl TabInfo {\n    pub fn from_tile<W: LayoutElement>(\n        tile: &Tile<W>,\n        position: Point<f64, Logical>,\n        is_active: bool,\n        is_urgent: bool,\n        config: &niri_config::TabIndicator,\n    ) -> Self {\n        let rules = tile.window().rules();\n        let rule = rules.tab_indicator;\n\n        let gradient_from_rule = || {\n            let (color, gradient) = if is_urgent {\n                (rule.urgent_color, rule.urgent_gradient)\n            } else if is_active {\n                (rule.active_color, rule.active_gradient)\n            } else {\n                (rule.inactive_color, rule.inactive_gradient)\n            };\n            let color = color.map(Gradient::from);\n            gradient.or(color)\n        };\n\n        let gradient_from_config = || {\n            let (color, gradient) = if is_urgent {\n                (config.urgent_color, config.urgent_gradient)\n            } else if is_active {\n                (config.active_color, config.active_gradient)\n            } else {\n                (config.inactive_color, config.inactive_gradient)\n            };\n            let color = color.map(Gradient::from);\n            gradient.or(color)\n        };\n\n        let gradient_from_border = || {\n            // Come up with tab indicator gradient matching the focus ring or the border, whichever\n            // one is enabled.\n            let focus_ring_config = tile.focus_ring().config();\n            let border_config = tile.border().config();\n            let config = if focus_ring_config.off {\n                border_config\n            } else {\n                focus_ring_config\n            };\n\n            let (color, gradient) = if is_urgent {\n                (config.urgent_color, config.urgent_gradient)\n            } else if is_active {\n                (config.active_color, config.active_gradient)\n            } else {\n                (config.inactive_color, config.inactive_gradient)\n            };\n            gradient.unwrap_or_else(|| Gradient::from(color))\n        };\n\n        let gradient = gradient_from_rule()\n            .or_else(gradient_from_config)\n            .unwrap_or_else(gradient_from_border);\n\n        let geometry = Rectangle::new(position, tile.animated_tile_size());\n\n        TabInfo { gradient, geometry }\n    }\n}\n"
  },
  {
    "path": "src/layout/tests/animations.rs",
    "content": "use std::fmt::Write as _;\n\nuse insta::assert_snapshot;\nuse niri_config::animations::{Curve, EasingParams, Kind};\n\nuse super::*;\n\nfn format_tiles(layout: &Layout<TestWindow>) -> String {\n    let mut buf = String::new();\n    let ws = layout.active_workspace().unwrap();\n    let mut tiles: Vec<_> = ws.tiles_with_render_positions().collect();\n\n    // We sort by id since that gives us a consistent order (from first opened to last), but we\n    // don't print the id since it's nondeterministic (the id is a global counter across all\n    // running tests in the same binary).\n    tiles.sort_by_key(|(tile, _, _)| tile.window().id());\n    for (tile, pos, _visible) in tiles {\n        let Size { w, h, .. } = tile.animated_tile_size();\n        let Point { x, y, .. } = pos;\n        writeln!(&mut buf, \"{w:>3.0} × {h:>3.0} at x:{x:>3.0} y:{y:>3.0}\").unwrap();\n    }\n    buf\n}\n\nfn make_options() -> Options {\n    const LINEAR: Kind = Kind::Easing(EasingParams {\n        duration_ms: 1000,\n        curve: Curve::Linear,\n    });\n\n    let mut options = Options {\n        layout: niri_config::Layout {\n            gaps: 0.0,\n            ..Default::default()\n        },\n        ..Options::default()\n    };\n    options.animations.window_resize.anim.kind = LINEAR;\n    options.animations.window_movement.0.kind = LINEAR;\n\n    options\n}\n\nfn set_up_two_in_column() -> Layout<TestWindow> {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::ConsumeWindowIntoColumn,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n\n    check_ops_with_options(make_options(), ops)\n}\n\n#[test]\nfn height_resize_animates_next_y() {\n    let mut layout = set_up_two_in_column();\n\n    let ops = [\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::AdjustFixed(-50),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 50)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // No time had passed yet, so we're at the initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n\n    // Advance the time halfway.\n    Op::AdvanceAnimations { msec_delta: 500 }.apply(&mut layout);\n\n    // Top window is half-resized at 75 px tall, bottom window is at y=75 matching it.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 ×  75 at x:  0 y:  0\n    200 × 200 at x:  0 y: 75\n    \");\n\n    // Advance the time to completion.\n    Op::AdvanceAnimations { msec_delta: 500 }.apply(&mut layout);\n\n    // Final state at 50 px.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 ×  50 at x:  0 y:  0\n    200 × 200 at x:  0 y: 50\n    \");\n}\n\n#[test]\nfn clientside_height_change_doesnt_animate() {\n    let mut layout = set_up_two_in_column();\n\n    // The initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n\n    let ops = [\n        // The top window shrinks by itself, without a niri-issued resize.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 50)),\n        },\n        // This does not start any animations.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // No time had passed yet, but we are at the final state right away.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 ×  50 at x:  0 y:  0\n    200 × 200 at x:  0 y: 50\n    \");\n}\n\n#[test]\nfn height_resize_and_back() {\n    let mut layout = set_up_two_in_column();\n\n    // The initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n\n    let ops = [\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The top window grows in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time halfway.\n        Op::AdvanceAnimations { msec_delta: 500 },\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Top window is half-resized at 150 px tall, bottom window is at y=150 matching it.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 150 at x:  0 y:  0\n    200 × 200 at x:  0 y:150\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This starts a new resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // No time had passed yet, and we expect no animation jumps, so this state matches the last.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 150 at x:  0 y:  0\n    200 × 200 at x:  0 y:150\n    \");\n\n    // Advance the time halfway.\n    Op::AdvanceAnimations { msec_delta: 500 }.apply(&mut layout);\n\n    // Halfway through at 125px.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 125 at x:  0 y:  0\n    200 × 200 at x:  0 y:125\n    \");\n\n    // Advance the time to completion.\n    Op::AdvanceAnimations { msec_delta: 500 }.apply(&mut layout);\n\n    // Final state back at 100px.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n}\n\n#[test]\nfn height_resize_and_cancel() {\n    let mut layout = set_up_two_in_column();\n\n    // The initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n\n    let ops = [\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The top window grows in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time slightly.\n        Op::AdvanceAnimations { msec_delta: 50 },\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Top window is half-resized at 105 px tall, bottom window is at y=105 matching it.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 105 at x:  0 y:  0\n    200 × 200 at x:  0 y:105\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This cancels the resize animation since the change of 5 px is less than the resize\n        // animation threshold.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Since the resize animation is cancelled, the height goes to the new value immediately. The Y\n    // position doesn't jump, instead the animation is offset to preserve the current position.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:105\n    \");\n\n    // Advance to the end of the move animation.\n    Op::AdvanceAnimations { msec_delta: 950 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n}\n\n#[test]\nfn height_resize_and_back_during_another_y_anim() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    let mut layout = check_ops_with_options(make_options(), ops);\n\n    // The initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Consume second window into column, starting the X/Y move anim down.\n    Op::ConsumeWindowIntoColumn.apply(&mut layout);\n\n    // No time had passed, so no change in coordinates yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Advance the time halfway.\n    Op::AdvanceAnimations { msec_delta: 500 }.apply(&mut layout);\n\n    // Second window halfway to the bottom.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x: 50 y: 50\n    \");\n\n    let ops = [\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The top window grows in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // No time had passed, so no change in state yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x: 50 y: 50\n    \");\n\n    // Advance the time a bit.\n    Op::AdvanceAnimations { msec_delta: 200 }.apply(&mut layout);\n\n    // X changed by 20, but y changed by 30 since the Y movement from the resize compounds with the\n    // Y movement from consume-into-column.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 120 at x:  0 y:  0\n    200 × 200 at x: 30 y: 80\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // No time had passed, so no change in state yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 120 at x:  0 y:  0\n    200 × 200 at x: 30 y: 80\n    \");\n\n    // Advance the time a bit. Both resize and consume movement are still ongoing.\n    Op::AdvanceAnimations { msec_delta: 200 }.apply(&mut layout);\n\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 116 at x:  0 y:  0\n    200 × 200 at x: 10 y: 84\n    \");\n\n    // Advance the time to complete the consume movement.\n    Op::AdvanceAnimations { msec_delta: 100 }.apply(&mut layout);\n\n    // The Y position is still lower than the height since the window started the resize-induced Y\n    // movement high up.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 114 at x:  0 y:  0\n    200 × 200 at x:  0 y: 86\n    \");\n\n    // Advance the time to complete the resize.\n    Op::AdvanceAnimations { msec_delta: 700 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n}\n\n#[test]\nfn height_resize_and_cancel_during_another_y_anim() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    let mut layout = check_ops_with_options(make_options(), ops);\n\n    // The initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Consume second window into column, starting the X/Y move anim down.\n    Op::ConsumeWindowIntoColumn.apply(&mut layout);\n\n    // No time had passed, so no change in coordinates yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Advance the time halfway.\n    Op::AdvanceAnimations { msec_delta: 500 }.apply(&mut layout);\n\n    // Second window halfway to the bottom.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x: 50 y: 50\n    \");\n\n    let ops = [\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The top window grows in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time slightly.\n        Op::AdvanceAnimations { msec_delta: 50 },\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // X changed by 5, but y changed by 8 since the Y movement from the resize compounds with the Y\n    // movement from consume-into-column.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 105 at x:  0 y:  0\n    200 × 200 at x: 45 y: 58\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This cancels the resize animation since the change of 5 px is less than the resize\n        // animation threshold.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Since the resize anim was cancelled, second window's Y anim is adjusted to preserve the\n    // current position while targeting the new final position.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x: 45 y: 58\n    \");\n\n    // Advance the time to complete the consume movement.\n    Op::AdvanceAnimations { msec_delta: 450 }.apply(&mut layout);\n\n    // Since we don't cancel the resize-induced part of the anim (in fact the move Y anim isn't\n    // split into parts, so there's no way to tell), it keeps going still.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y: 78\n    \");\n\n    // Advance the time to complete the resize-induced anim.\n    Op::AdvanceAnimations { msec_delta: 550 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n}\n\n#[test]\nfn height_resize_before_another_y_anim_then_back() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The top window grows in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time a bit.\n        Op::AdvanceAnimations { msec_delta: 200 },\n    ];\n    let mut layout = check_ops_with_options(make_options(), ops);\n\n    // The resize is in progress.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 120 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Consume second window into column, starting the X/Y move anim down.\n    Op::ConsumeWindowIntoColumn.apply(&mut layout);\n\n    // No time had passed, so no change in coordinates yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 120 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Advance the time halfway.\n    Op::AdvanceAnimations { msec_delta: 600 }.apply(&mut layout);\n\n    // Second window halfway to the bottom. Since consume happened after the start of the first\n    // window's resize, the second window's Y is unaffected by it and is animating towards the\n    // final position right away.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 180 at x:  0 y:  0\n    200 × 200 at x: 40 y:120\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // No time had passed, so no change in state yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 180 at x:  0 y:  0\n    200 × 200 at x: 40 y:120\n    \");\n\n    // Advance the time a bit. Both resize and consume movement are still ongoing.\n    Op::AdvanceAnimations { msec_delta: 200 }.apply(&mut layout);\n\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 164 at x:  0 y:  0\n    200 × 200 at x: 20 y:116\n    \");\n\n    // Advance the time to complete the consume movement.\n    Op::AdvanceAnimations { msec_delta: 200 }.apply(&mut layout);\n\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 148 at x:  0 y:  0\n    200 × 200 at x:  0 y:112\n    \");\n\n    // Advance the time to complete the resize.\n    Op::AdvanceAnimations { msec_delta: 600 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n}\n\n#[test]\nfn height_resize_before_another_y_anim_then_cancel() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The top window grows in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time a bit.\n        Op::AdvanceAnimations { msec_delta: 20 },\n    ];\n    let mut layout = check_ops_with_options(make_options(), ops);\n\n    // The resize is in progress.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 102 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Consume second window into column, starting the X/Y move anim down.\n    Op::ConsumeWindowIntoColumn.apply(&mut layout);\n\n    // No time had passed, so no change in coordinates yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 102 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Advance the time a little.\n    Op::AdvanceAnimations { msec_delta: 20 }.apply(&mut layout);\n\n    // Second window on its way to the bottom.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 104 at x:  0 y:  0\n    200 × 200 at x: 98 y:  4\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This cancels the resize animation since the change of 4 px is less than the resize\n        // animation threshold.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // The second window's trajectory readjusts to the new final position at 100 px, without jumps.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x: 98 y:  4\n    \");\n\n    // Advance the time to complete the consume movement.\n    Op::AdvanceAnimations { msec_delta: 980 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n}\n\n#[test]\nfn clientside_height_change_during_another_y_anim() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n        Op::ConsumeWindowIntoColumn,\n        // Clear the animate next configure flag.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time a bit.\n        Op::AdvanceAnimations { msec_delta: 200 },\n    ];\n    let mut layout = check_ops_with_options(make_options(), ops);\n\n    // Second window on its way to the bottom.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x: 80 y: 20\n    \");\n\n    let ops = [\n        // The top window suddenly grows.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // The second window's trajectory readjusts to the new final position at 200 px, without jumps.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 200 at x:  0 y:  0\n    200 × 200 at x: 80 y: 20\n    \");\n\n    // Advance the time to complete the consume movement.\n    Op::AdvanceAnimations { msec_delta: 800 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 200 at x:  0 y:  0\n    200 × 200 at x:  0 y:200\n    \");\n}\n\n#[test]\nfn height_resize_cancel_with_stationary_second_window() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n        // Issue a resize.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The top window grows in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 200)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time a bit.\n        Op::AdvanceAnimations { msec_delta: 20 },\n    ];\n    let mut options = make_options();\n    // Window movement will happen instantly.\n    options.animations.window_movement.0.off = true;\n    let mut layout = check_ops_with_options(options, ops);\n\n    // The resize is in progress.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 102 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Consume second window into column, starting the X/Y move anim down.\n    Op::ConsumeWindowIntoColumn.apply(&mut layout);\n\n    // No time had passed, so no change in coordinates yet.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 102 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Advance the time a little.\n    Op::AdvanceAnimations { msec_delta: 20 }.apply(&mut layout);\n\n    // The window movement anim is off, so the second window is already at the bottom. Since\n    // consume started after the resize, the second window is unaffected by the resize-induced Y\n    // movement, and sits at the final position at 200 px.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 104 at x:  0 y:  0\n    200 × 200 at x:  0 y:200\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response, the bottom remains as is.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This cancels the resize animation since the change of 4 px is less than the resize\n        // animation threshold.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // This causes the second window to jump down, which is correct because it hadn't been in an\n    // animation, and as far as it's concerned, this is the same case as a window just deciding to\n    // do a clientside resize on its own, which is not animated.\n    //\n    // Since the resize is also cancelled, this is the final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n}\n\n#[test]\nfn width_resize_and_cancel() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnLeft,\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    let mut layout = check_ops_with_options(make_options(), ops);\n\n    // The initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    let ops = [\n        // Issue a resize.\n        Op::SetWindowWidth {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        // The left window grows in response.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(200, 100)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time slightly.\n        Op::AdvanceAnimations { msec_delta: 50 },\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Left window is half-resized at 105 px wide, right window is at x=105 matching it.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    105 × 100 at x:  0 y:  0\n    200 × 200 at x:105 y:  0\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowWidth {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This cancels the resize animation since the change of 5 px is less than the resize\n        // animation threshold.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Since the resize animation is cancelled, the width goes to the new value immediately. The X\n    // position doesn't jump, instead the animation is restarted to preserve the current position.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:105 y:  0\n    \");\n\n    // Advance to the end of the move animation.\n    Op::AdvanceAnimations { msec_delta: 1000 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n}\n\n#[test]\nfn width_resize_and_cancel_of_column_to_the_left() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        Op::SetForcedSize {\n            id: 2,\n            size: Some(Size::new(200, 200)),\n        },\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    let mut layout = check_ops_with_options(make_options(), ops);\n\n    // The initial state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    let ops = [\n        // Issue a resize.\n        Op::SetWindowWidth {\n            id: Some(1),\n            change: SizeChange::SetFixed(200),\n        },\n        // The left window grows in response.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(200, 100)),\n        },\n        // This starts the resize animation.\n        Op::Communicate(1),\n        Op::Communicate(2),\n        // Advance the time slightly.\n        Op::AdvanceAnimations { msec_delta: 50 },\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Left window is half-resized at 105 px wide, it's at x=-5 matching the right edge position.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    105 × 100 at x: -5 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    let ops = [\n        // Issue a resize back.\n        Op::SetWindowWidth {\n            id: Some(1),\n            change: SizeChange::SetFixed(100),\n        },\n        // The top window shrinks in response.\n        Op::SetForcedSize {\n            id: 1,\n            size: Some(Size::new(100, 100)),\n        },\n        // This cancels the resize animation since the change of 5 px is less than the resize\n        // animation threshold.\n        Op::Communicate(1),\n        Op::Communicate(2),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Since the resize animation is cancelled, the width goes to the new value immediately. The X\n    // position doesn't jump, instead the animation is restarted to preserve the current position.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x: -5 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n\n    // Advance to the end of the move animation.\n    Op::AdvanceAnimations { msec_delta: 1000 }.apply(&mut layout);\n\n    // Final state.\n    assert_snapshot!(format_tiles(&layout), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:100 y:  0\n    \");\n}\n"
  },
  {
    "path": "src/layout/tests/fullscreen.rs",
    "content": "use insta::assert_snapshot;\n\nuse super::*;\n\n#[test]\nfn fullscreen() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FullscreenWindow(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_window_in_column() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SetFullscreenWindow {\n            window: 2,\n            is_fullscreen: false,\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_view_offset_not_reset_on_removal() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FullscreenWindow(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeOrExpelWindowRight { id: None },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_view_offset_not_reset_on_consume() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FullscreenWindow(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeWindowIntoColumn,\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_view_offset_not_reset_on_quick_double_toggle() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FullscreenWindow(0),\n        Op::FullscreenWindow(0),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_view_offset_set_on_fullscreening_inactive_tile_in_column() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::FullscreenWindow(0),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_view_offset_not_reset_on_gesture() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FullscreenWindow(1),\n        Op::ViewOffsetGestureBegin {\n            output_idx: 1,\n            workspace_idx: None,\n            is_touchpad: true,\n        },\n        Op::ViewOffsetGestureEnd {\n            is_touchpad: Some(true),\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn one_window_in_column_becomes_weight_1_after_fullscreen() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        Op::Communicate(2),\n        Op::FocusWindowUp,\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        Op::Communicate(1),\n        Op::CloseWindow(0),\n        Op::FullscreenWindow(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn disable_tabbed_mode_in_fullscreen() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::ToggleColumnTabbedDisplay,\n        Op::FullscreenWindow(0),\n        Op::ToggleColumnTabbedDisplay,\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_with_large_border() {\n    let ops = [\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FullscreenWindow(0),\n        Op::Communicate(0),\n        Op::FullscreenWindow(0),\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            border: niri_config::Border {\n                off: false,\n                width: 10000.,\n                ..Default::default()\n            },\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn fullscreen_to_windowed_fullscreen() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FullscreenWindow(0),\n        Op::Communicate(0), // Make sure it goes into fullscreen.\n        Op::ToggleWindowedFullscreen(0),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn windowed_fullscreen_to_fullscreen() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FullscreenWindow(0),\n        Op::Communicate(0),              // Commit fullscreen state.\n        Op::ToggleWindowedFullscreen(0), // Switch is_fullscreen() to false.\n        Op::FullscreenWindow(0),         // Switch is_fullscreen() back to true.\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn move_pending_unfullscreen_window_out_of_active_column() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FullscreenWindow(1),\n        Op::Communicate(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeWindowIntoColumn,\n        // Window 1 is now pending unfullscreen.\n        // Moving it out should reset view_offset_before_fullscreen.\n        Op::MoveWindowToWorkspaceDown(true),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn move_unfocused_pending_unfullscreen_window_out_of_active_column() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FullscreenWindow(1),\n        Op::Communicate(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeWindowIntoColumn,\n        // Window 1 is now pending unfullscreen.\n        // Moving it out should reset view_offset_before_fullscreen.\n        Op::FocusWindowDown,\n        Op::MoveWindowToWorkspace {\n            window_id: Some(1),\n            workspace_idx: 1,\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_resize_on_pending_unfullscreen_column() {\n    let ops = [\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FullscreenWindow(2),\n        Op::Communicate(2),\n        Op::SetFullscreenWindow {\n            window: 2,\n            is_fullscreen: false,\n        },\n        Op::InteractiveResizeBegin {\n            window: 2,\n            edges: ResizeEdge::RIGHT,\n        },\n        Op::Communicate(2),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_move_unfullscreen_to_floating_stops_dnd_scroll() {\n    let ops = [\n        Op::AddOutput(3),\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                ..TestWindowParams::new(4)\n            },\n        },\n        // This moves the window to tiling.\n        Op::SetFullscreenWindow {\n            window: 4,\n            is_fullscreen: true,\n        },\n        // This starts a DnD scroll since we're dragging a tiled window.\n        Op::InteractiveMoveBegin {\n            window: 4,\n            output_idx: 3,\n            px: 0.0,\n            py: 0.0,\n        },\n        // This will cause the window to unfullscreen to floating, and should stop the DnD scroll\n        // since we're no longer dragging a tiled window, but rather a floating one.\n        Op::InteractiveMoveUpdate {\n            window: 4,\n            dx: 0.0,\n            dy: 15035.31210741684,\n            output_idx: 3,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::InteractiveMoveEnd { window: 4 },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_move_restore_to_floating_animates_view_offset() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        // Toggle window 1 to floating.\n        Op::FocusWindow(1),\n        Op::ToggleWindowFloating { id: None },\n        // Fullscreen window 1 - it moves to scrolling with restore_to_floating = true.\n        Op::FullscreenWindow(1),\n        Op::Communicate(1),\n        Op::CompleteAnimations,\n    ];\n\n    let mut layout = check_ops(ops);\n\n    // Verify window 1 is in scrolling and has restore_to_floating = true.\n    let scrolling = layout.active_workspace().unwrap().scrolling();\n    let tile1 = scrolling.tiles().find(|t| *t.window().id() == 1).unwrap();\n    assert!(\n        tile1.restore_to_floating,\n        \"window 1 should have restore_to_floating = true\"\n    );\n\n    let ops = [\n        // Start interactive move on window 1.\n        Op::InteractiveMoveBegin {\n            window: 1,\n            output_idx: 1,\n            px: 100.,\n            py: 100.,\n        },\n        // Update with a large delta to trigger the unmaximize.\n        Op::InteractiveMoveUpdate {\n            window: 1,\n            dx: 1000.,\n            dy: 1000.,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Window 1 should now be removed from the workspace (in the interactive move state).\n    // Window 2 should be the only window in the scrolling space.\n    let scrolling = layout.active_workspace().unwrap().scrolling();\n    assert_eq!(scrolling.tiles().count(), 1);\n    assert!(scrolling.tiles().next().unwrap().window().id() == &2);\n\n    // The view offset should be animating to show window 2.\n    assert!(scrolling.view_offset().is_animation_ongoing());\n}\n\n#[test]\nfn unfullscreen_view_offset_not_reset_during_dnd_gesture() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::FullscreenWindow(3),\n        Op::Communicate(3),\n        Op::DndUpdate {\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::FullscreenWindow(3),\n        Op::Communicate(3),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_view_offset_not_reset_during_gesture() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::FullscreenWindow(3),\n        Op::Communicate(3),\n        Op::ViewOffsetGestureBegin {\n            output_idx: 1,\n            workspace_idx: None,\n            is_touchpad: false,\n        },\n        Op::FullscreenWindow(3),\n        Op::Communicate(3),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_view_offset_not_reset_during_ongoing_gesture() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::ViewOffsetGestureBegin {\n            output_idx: 1,\n            workspace_idx: None,\n            is_touchpad: false,\n        },\n        Op::FullscreenWindow(3),\n        Op::Communicate(3),\n        Op::FullscreenWindow(3),\n        Op::Communicate(3),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn unfullscreen_preserves_view_pos() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n    ];\n\n    let mut layout = check_ops(ops);\n\n    // View pos is looking at the first window.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n\n    let ops = [\n        Op::FullscreenWindow(2),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos = width of first window + gap.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"116\");\n\n    let ops = [\n        Op::FullscreenWindow(2),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos is back to showing the first window.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n}\n\n#[test]\nfn unfullscreen_of_tabbed_preserves_view_pos() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SetColumnDisplay(ColumnDisplay::Tabbed),\n        // Get view pos back on the first window.\n        Op::FocusColumnLeft,\n        Op::FocusColumnRight,\n    ];\n\n    let mut layout = check_ops(ops);\n\n    // View pos is looking at the first window.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n\n    let ops = [\n        Op::FullscreenWindow(2),\n        Op::Communicate(2),\n        Op::Communicate(3),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos = width of first window + gap.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"116\");\n\n    let ops = [\n        Op::FullscreenWindow(3),\n        Op::Communicate(3),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos is still on the second column because the second tile hasn't unfullscreened yet.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"116\");\n\n    let ops = [Op::Communicate(2), Op::CompleteAnimations];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos is back to showing the first window.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n}\n\n#[test]\nfn unfullscreen_of_tabbed_via_change_to_normal_preserves_view_pos() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SetColumnDisplay(ColumnDisplay::Tabbed),\n        // Get view pos back on the first window.\n        Op::FocusColumnLeft,\n        Op::FocusColumnRight,\n    ];\n\n    let mut layout = check_ops(ops);\n\n    // View pos is looking at the first window.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n\n    let ops = [\n        Op::FullscreenWindow(2),\n        Op::Communicate(2),\n        Op::Communicate(3),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos = width of first window + gap.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"116\");\n\n    let ops = [\n        Op::SetColumnDisplay(ColumnDisplay::Normal),\n        Op::Communicate(3),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos is still on the second column because the second tile hasn't unfullscreened yet.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"116\");\n\n    let ops = [Op::Communicate(2), Op::CompleteAnimations];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos is back to showing the first window.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n}\n\n#[test]\nfn removing_only_fullscreen_tile_updates_view_offset() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SetColumnDisplay(ColumnDisplay::Tabbed),\n        Op::CompleteAnimations,\n    ];\n\n    let mut layout = check_ops(ops);\n\n    // View pos with gap.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n\n    let ops = [\n        Op::FullscreenWindow(2),\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos without gap because we went fullscreen.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"0\");\n\n    let ops = [\n        Op::FullscreenWindow(2),\n        // The active window responds, the other tabbed window doesn't yet.\n        Op::Communicate(2),\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos without gap because other tile is still fullscreen.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"0\");\n\n    let ops = [\n        // Expel the fullscreen window from the column, changing the column to non-fullscreen.\n        Op::ConsumeOrExpelWindowRight { id: Some(1) },\n        Op::CompleteAnimations,\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // View pos should include gap now that the column is no longer fullscreen.\n    // FIXME: currently, removing a tile doesn't cause the view offset to update.\n    assert_snapshot!(layout.active_workspace().unwrap().scrolling().view_pos(), @\"0\");\n}\n"
  },
  {
    "path": "src/layout/tests.rs",
    "content": "use std::cell::{Cell, OnceCell, RefCell};\n\nuse niri_config::utils::{Flag, MergeWith as _};\nuse niri_config::workspace::WorkspaceName;\nuse niri_config::{\n    CenterFocusedColumn, FloatOrInt, OutputName, Struts, TabIndicatorLength, TabIndicatorPosition,\n    WorkspaceReference,\n};\nuse proptest::prelude::*;\nuse proptest_derive::Arbitrary;\nuse smithay::output::{Mode, PhysicalProperties, Subpixel};\nuse smithay::utils::Rectangle;\n\nuse super::*;\n\nmod animations;\nmod fullscreen;\n\nimpl<W: LayoutElement> Default for Layout<W> {\n    fn default() -> Self {\n        Self::with_options(Clock::with_time(Duration::ZERO), Default::default())\n    }\n}\n\n#[derive(Debug)]\nstruct TestWindowInner {\n    id: usize,\n    parent_id: Cell<Option<usize>>,\n    bbox: Cell<Rectangle<i32, Logical>>,\n    initial_bbox: Rectangle<i32, Logical>,\n    requested_size: Cell<Option<Size<i32, Logical>>>,\n    // Emulates the window ignoring the compositor-provided size.\n    forced_size: Cell<Option<Size<i32, Logical>>>,\n    min_size: Size<i32, Logical>,\n    max_size: Size<i32, Logical>,\n    pending_sizing_mode: Cell<SizingMode>,\n    pending_activated: Cell<bool>,\n    sizing_mode: Cell<SizingMode>,\n    is_windowed_fullscreen: Cell<bool>,\n    is_pending_windowed_fullscreen: Cell<bool>,\n    animate_next_configure: Cell<bool>,\n    animation_snapshot: RefCell<Option<LayoutElementRenderSnapshot>>,\n    rules: ResolvedWindowRules,\n}\n\n#[derive(Debug, Clone)]\nstruct TestWindow(Rc<TestWindowInner>);\n\n#[derive(Debug, Clone, Arbitrary)]\nstruct TestWindowParams {\n    #[proptest(strategy = \"1..=5usize\")]\n    id: usize,\n    #[proptest(strategy = \"arbitrary_parent_id()\")]\n    parent_id: Option<usize>,\n    is_floating: bool,\n    #[proptest(strategy = \"arbitrary_bbox()\")]\n    bbox: Rectangle<i32, Logical>,\n    #[proptest(strategy = \"arbitrary_min_max_size()\")]\n    min_max_size: (Size<i32, Logical>, Size<i32, Logical>),\n    #[proptest(strategy = \"prop::option::of(arbitrary_rules())\")]\n    rules: Option<ResolvedWindowRules>,\n}\n\nimpl TestWindowParams {\n    pub fn new(id: usize) -> Self {\n        Self {\n            id,\n            parent_id: None,\n            is_floating: false,\n            bbox: Rectangle::from_size(Size::from((100, 200))),\n            min_max_size: Default::default(),\n            rules: None,\n        }\n    }\n}\n\nimpl TestWindow {\n    fn new(params: TestWindowParams) -> Self {\n        Self(Rc::new(TestWindowInner {\n            id: params.id,\n            parent_id: Cell::new(params.parent_id),\n            bbox: Cell::new(params.bbox),\n            initial_bbox: params.bbox,\n            requested_size: Cell::new(None),\n            forced_size: Cell::new(None),\n            min_size: params.min_max_size.0,\n            max_size: params.min_max_size.1,\n            pending_sizing_mode: Cell::new(SizingMode::Normal),\n            pending_activated: Cell::new(false),\n            sizing_mode: Cell::new(SizingMode::Normal),\n            is_windowed_fullscreen: Cell::new(false),\n            is_pending_windowed_fullscreen: Cell::new(false),\n            animate_next_configure: Cell::new(false),\n            animation_snapshot: RefCell::new(None),\n            rules: params.rules.unwrap_or_default(),\n        }))\n    }\n\n    fn communicate(&self) -> bool {\n        let mut changed = false;\n\n        let size = self.0.forced_size.get().or(self.0.requested_size.get());\n        if let Some(size) = size {\n            assert!(size.w >= 0);\n            assert!(size.h >= 0);\n\n            let mut new_bbox = self.0.initial_bbox;\n            if size.w != 0 {\n                new_bbox.size.w = size.w;\n            }\n            if size.h != 0 {\n                new_bbox.size.h = size.h;\n            }\n\n            if self.0.bbox.get() != new_bbox {\n                if self.0.animate_next_configure.get() {\n                    self.0.animation_snapshot.replace(Some(RenderSnapshot {\n                        contents: Vec::new(),\n                        blocked_out_contents: Vec::new(),\n                        block_out_from: None,\n                        size: self.0.bbox.get().size.to_f64(),\n                        texture: OnceCell::new(),\n                        blocked_out_texture: OnceCell::new(),\n                    }));\n                }\n\n                self.0.bbox.set(new_bbox);\n                changed = true;\n            }\n        }\n\n        self.0.animate_next_configure.set(false);\n\n        if self.0.sizing_mode.get() != self.0.pending_sizing_mode.get() {\n            self.0.sizing_mode.set(self.0.pending_sizing_mode.get());\n            changed = true;\n        }\n\n        if self.0.is_windowed_fullscreen.get() != self.0.is_pending_windowed_fullscreen.get() {\n            self.0\n                .is_windowed_fullscreen\n                .set(self.0.is_pending_windowed_fullscreen.get());\n            changed = true;\n        }\n\n        changed\n    }\n}\n\nimpl LayoutElement for TestWindow {\n    type Id = usize;\n\n    fn id(&self) -> &Self::Id {\n        &self.0.id\n    }\n\n    fn size(&self) -> Size<i32, Logical> {\n        self.0.bbox.get().size\n    }\n\n    fn buf_loc(&self) -> Point<i32, Logical> {\n        (0, 0).into()\n    }\n\n    fn is_in_input_region(&self, _point: Point<f64, Logical>) -> bool {\n        false\n    }\n\n    fn request_size(\n        &mut self,\n        size: Size<i32, Logical>,\n        mode: SizingMode,\n        _animate: bool,\n        _transaction: Option<Transaction>,\n    ) {\n        if self.0.requested_size.get() != Some(size) {\n            self.0.requested_size.set(Some(size));\n            self.0.animate_next_configure.set(true);\n        }\n\n        self.0.pending_sizing_mode.set(mode);\n\n        if mode.is_fullscreen() {\n            self.0.is_pending_windowed_fullscreen.set(false);\n        }\n    }\n\n    fn min_size(&self) -> Size<i32, Logical> {\n        self.0.min_size\n    }\n\n    fn max_size(&self) -> Size<i32, Logical> {\n        self.0.max_size\n    }\n\n    fn is_wl_surface(&self, _wl_surface: &WlSurface) -> bool {\n        false\n    }\n\n    fn set_preferred_scale_transform(&self, _scale: output::Scale, _transform: Transform) {}\n\n    fn has_ssd(&self) -> bool {\n        false\n    }\n\n    fn output_enter(&self, _output: &Output) {}\n\n    fn output_leave(&self, _output: &Output) {}\n\n    fn set_offscreen_data(&self, _data: Option<OffscreenData>) {}\n\n    fn set_activated(&mut self, active: bool) {\n        self.0.pending_activated.set(active);\n    }\n\n    fn set_bounds(&self, _bounds: Size<i32, Logical>) {}\n\n    fn is_ignoring_opacity_window_rule(&self) -> bool {\n        false\n    }\n\n    fn configure_intent(&self) -> ConfigureIntent {\n        ConfigureIntent::CanSend\n    }\n\n    fn send_pending_configure(&mut self) {}\n\n    fn set_active_in_column(&mut self, _active: bool) {}\n\n    fn set_floating(&mut self, _floating: bool) {}\n\n    fn sizing_mode(&self) -> SizingMode {\n        self.0.sizing_mode.get()\n    }\n\n    fn pending_sizing_mode(&self) -> SizingMode {\n        self.0.pending_sizing_mode.get()\n    }\n\n    fn requested_size(&self) -> Option<Size<i32, Logical>> {\n        self.0.requested_size.get()\n    }\n\n    fn is_pending_windowed_fullscreen(&self) -> bool {\n        self.0.is_pending_windowed_fullscreen.get()\n    }\n\n    fn request_windowed_fullscreen(&mut self, value: bool) {\n        self.0.is_pending_windowed_fullscreen.set(value);\n    }\n\n    fn is_child_of(&self, parent: &Self) -> bool {\n        self.0.parent_id.get() == Some(parent.0.id)\n    }\n\n    fn refresh(&self) {}\n\n    fn rules(&self) -> &ResolvedWindowRules {\n        &self.0.rules\n    }\n\n    fn take_animation_snapshot(&mut self) -> Option<LayoutElementRenderSnapshot> {\n        self.0.animation_snapshot.take()\n    }\n\n    fn set_interactive_resize(&mut self, _data: Option<InteractiveResizeData>) {}\n\n    fn cancel_interactive_resize(&mut self) {}\n\n    fn on_commit(&mut self, _serial: Serial) {}\n\n    fn interactive_resize_data(&self) -> Option<InteractiveResizeData> {\n        None\n    }\n\n    fn is_urgent(&self) -> bool {\n        false\n    }\n}\n\nfn arbitrary_size() -> impl Strategy<Value = Size<i32, Logical>> {\n    any::<(u16, u16)>().prop_map(|(w, h)| Size::from((w.max(1).into(), h.max(1).into())))\n}\n\nfn arbitrary_bbox() -> impl Strategy<Value = Rectangle<i32, Logical>> {\n    any::<(i16, i16, u16, u16)>().prop_map(|(x, y, w, h)| {\n        let loc: Point<i32, _> = Point::from((x.into(), y.into()));\n        let size: Size<i32, _> = Size::from((w.max(1).into(), h.max(1).into()));\n        Rectangle::new(loc, size)\n    })\n}\n\nfn arbitrary_size_change() -> impl Strategy<Value = SizeChange> {\n    prop_oneof![\n        (0..).prop_map(SizeChange::SetFixed),\n        (0f64..).prop_map(SizeChange::SetProportion),\n        any::<i32>().prop_map(SizeChange::AdjustFixed),\n        any::<f64>().prop_map(SizeChange::AdjustProportion),\n        // Interactive resize can have negative values here.\n        Just(SizeChange::SetFixed(-100)),\n    ]\n}\n\nfn arbitrary_position_change() -> impl Strategy<Value = PositionChange> {\n    prop_oneof![\n        (-1000f64..1000f64).prop_map(PositionChange::SetFixed),\n        any::<f64>().prop_map(PositionChange::SetProportion),\n        (-1000f64..1000f64).prop_map(PositionChange::AdjustFixed),\n        any::<f64>().prop_map(PositionChange::AdjustProportion),\n        any::<f64>().prop_map(PositionChange::SetFixed),\n        any::<f64>().prop_map(PositionChange::AdjustFixed),\n    ]\n}\n\nfn arbitrary_min_max() -> impl Strategy<Value = (i32, i32)> {\n    prop_oneof![\n        Just((0, 0)),\n        (1..65536).prop_map(|n| (n, n)),\n        (1..65536).prop_map(|min| (min, 0)),\n        (1..).prop_map(|max| (0, max)),\n        (1..65536, 1..).prop_map(|(min, max): (i32, i32)| (min, max.max(min))),\n    ]\n}\n\nfn arbitrary_min_max_size() -> impl Strategy<Value = (Size<i32, Logical>, Size<i32, Logical>)> {\n    prop_oneof![\n        5 => (arbitrary_min_max(), arbitrary_min_max()).prop_map(\n            |((min_w, max_w), (min_h, max_h))| {\n                let min_size = Size::from((min_w, min_h));\n                let max_size = Size::from((max_w, max_h));\n                (min_size, max_size)\n            },\n        ),\n        1 => arbitrary_min_max().prop_map(|(w, h)| {\n            let size = Size::from((w, h));\n            (size, size)\n        }),\n    ]\n}\n\nprop_compose! {\n    fn arbitrary_rules()(\n        focus_ring in arbitrary_focus_ring(),\n        border in arbitrary_border(),\n    ) -> ResolvedWindowRules {\n        ResolvedWindowRules {\n            focus_ring,\n            border,\n            ..ResolvedWindowRules::default()\n        }\n    }\n}\n\nfn arbitrary_view_offset_gesture_delta() -> impl Strategy<Value = f64> {\n    prop_oneof![(-10f64..10f64), (-50000f64..50000f64),]\n}\n\nfn arbitrary_resize_edge() -> impl Strategy<Value = ResizeEdge> {\n    prop_oneof![\n        Just(ResizeEdge::RIGHT),\n        Just(ResizeEdge::BOTTOM),\n        Just(ResizeEdge::LEFT),\n        Just(ResizeEdge::TOP),\n        Just(ResizeEdge::BOTTOM_RIGHT),\n        Just(ResizeEdge::BOTTOM_LEFT),\n        Just(ResizeEdge::TOP_RIGHT),\n        Just(ResizeEdge::TOP_LEFT),\n        Just(ResizeEdge::empty()),\n    ]\n}\n\nfn arbitrary_scale() -> impl Strategy<Value = f64> {\n    prop_oneof![Just(1.), Just(1.5), Just(2.),]\n}\n\nfn arbitrary_msec_delta() -> impl Strategy<Value = i32> {\n    prop_oneof![\n        1 => Just(-1000),\n        2 => Just(-10),\n        1 => Just(0),\n        2 => Just(10),\n        6 => Just(1000),\n    ]\n}\n\nfn arbitrary_parent_id() -> impl Strategy<Value = Option<usize>> {\n    prop_oneof![\n        5 => Just(None),\n        1 => prop::option::of(1..=5usize),\n    ]\n}\n\nfn arbitrary_scroll_direction() -> impl Strategy<Value = ScrollDirection> {\n    prop_oneof![Just(ScrollDirection::Left), Just(ScrollDirection::Right)]\n}\n\nfn arbitrary_column_display() -> impl Strategy<Value = ColumnDisplay> {\n    prop_oneof![Just(ColumnDisplay::Normal), Just(ColumnDisplay::Tabbed)]\n}\n\n#[derive(Debug, Clone, Arbitrary)]\nenum Op {\n    AddOutput(#[proptest(strategy = \"1..=5usize\")] usize),\n    AddScaledOutput {\n        #[proptest(strategy = \"1..=5usize\")]\n        id: usize,\n        #[proptest(strategy = \"arbitrary_scale()\")]\n        scale: f64,\n        #[proptest(strategy = \"prop::option::of(arbitrary_layout_part().prop_map(Box::new))\")]\n        layout_config: Option<Box<niri_config::LayoutPart>>,\n    },\n    RemoveOutput(#[proptest(strategy = \"1..=5usize\")] usize),\n    FocusOutput(#[proptest(strategy = \"1..=5usize\")] usize),\n    UpdateOutputLayoutConfig {\n        #[proptest(strategy = \"1..=5usize\")]\n        id: usize,\n        #[proptest(strategy = \"prop::option::of(arbitrary_layout_part().prop_map(Box::new))\")]\n        layout_config: Option<Box<niri_config::LayoutPart>>,\n    },\n    AddNamedWorkspace {\n        #[proptest(strategy = \"1..=5usize\")]\n        ws_name: usize,\n        #[proptest(strategy = \"prop::option::of(1..=5usize)\")]\n        output_name: Option<usize>,\n        #[proptest(strategy = \"prop::option::of(arbitrary_layout_part().prop_map(Box::new))\")]\n        layout_config: Option<Box<niri_config::LayoutPart>>,\n    },\n    UnnameWorkspace {\n        #[proptest(strategy = \"1..=5usize\")]\n        ws_name: usize,\n    },\n    UpdateWorkspaceLayoutConfig {\n        #[proptest(strategy = \"1..=5usize\")]\n        ws_name: usize,\n        #[proptest(strategy = \"prop::option::of(arbitrary_layout_part().prop_map(Box::new))\")]\n        layout_config: Option<Box<niri_config::LayoutPart>>,\n    },\n    AddWindow {\n        params: TestWindowParams,\n    },\n    AddWindowNextTo {\n        params: TestWindowParams,\n        #[proptest(strategy = \"1..=5usize\")]\n        next_to_id: usize,\n    },\n    AddWindowToNamedWorkspace {\n        params: TestWindowParams,\n        #[proptest(strategy = \"1..=5usize\")]\n        ws_name: usize,\n    },\n    CloseWindow(#[proptest(strategy = \"1..=5usize\")] usize),\n    FullscreenWindow(#[proptest(strategy = \"1..=5usize\")] usize),\n    SetFullscreenWindow {\n        #[proptest(strategy = \"1..=5usize\")]\n        window: usize,\n        is_fullscreen: bool,\n    },\n    ToggleWindowedFullscreen(#[proptest(strategy = \"1..=5usize\")] usize),\n    FocusColumnLeft,\n    FocusColumnRight,\n    FocusColumnFirst,\n    FocusColumnLast,\n    FocusColumnRightOrFirst,\n    FocusColumnLeftOrLast,\n    FocusColumn(#[proptest(strategy = \"1..=5usize\")] usize),\n    FocusWindowOrMonitorUp(#[proptest(strategy = \"1..=2u8\")] u8),\n    FocusWindowOrMonitorDown(#[proptest(strategy = \"1..=2u8\")] u8),\n    FocusColumnOrMonitorLeft(#[proptest(strategy = \"1..=2u8\")] u8),\n    FocusColumnOrMonitorRight(#[proptest(strategy = \"1..=2u8\")] u8),\n    FocusWindowDown,\n    FocusWindowUp,\n    FocusWindowDownOrColumnLeft,\n    FocusWindowDownOrColumnRight,\n    FocusWindowUpOrColumnLeft,\n    FocusWindowUpOrColumnRight,\n    FocusWindowOrWorkspaceDown,\n    FocusWindowOrWorkspaceUp,\n    FocusWindow(#[proptest(strategy = \"1..=5usize\")] usize),\n    FocusWindowInColumn(#[proptest(strategy = \"1..=5u8\")] u8),\n    FocusWindowTop,\n    FocusWindowBottom,\n    FocusWindowDownOrTop,\n    FocusWindowUpOrBottom,\n    MoveColumnLeft,\n    MoveColumnRight,\n    MoveColumnToFirst,\n    MoveColumnToLast,\n    MoveColumnLeftOrToMonitorLeft(#[proptest(strategy = \"1..=2u8\")] u8),\n    MoveColumnRightOrToMonitorRight(#[proptest(strategy = \"1..=2u8\")] u8),\n    MoveColumnToIndex(#[proptest(strategy = \"1..=5usize\")] usize),\n    MoveWindowDown,\n    MoveWindowUp,\n    MoveWindowDownOrToWorkspaceDown,\n    MoveWindowUpOrToWorkspaceUp,\n    ConsumeOrExpelWindowLeft {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    ConsumeOrExpelWindowRight {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    ConsumeWindowIntoColumn,\n    ExpelWindowFromColumn,\n    SwapWindowInDirection(#[proptest(strategy = \"arbitrary_scroll_direction()\")] ScrollDirection),\n    ToggleColumnTabbedDisplay,\n    SetColumnDisplay(#[proptest(strategy = \"arbitrary_column_display()\")] ColumnDisplay),\n    CenterColumn,\n    CenterWindow {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    CenterVisibleColumns,\n    FocusWorkspaceDown,\n    FocusWorkspaceUp,\n    FocusWorkspace(#[proptest(strategy = \"0..=4usize\")] usize),\n    FocusWorkspaceAutoBackAndForth(#[proptest(strategy = \"0..=4usize\")] usize),\n    FocusWorkspacePrevious,\n    MoveWindowToWorkspaceDown(bool),\n    MoveWindowToWorkspaceUp(bool),\n    MoveWindowToWorkspace {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        window_id: Option<usize>,\n        #[proptest(strategy = \"0..=4usize\")]\n        workspace_idx: usize,\n    },\n    MoveColumnToWorkspaceDown(bool),\n    MoveColumnToWorkspaceUp(bool),\n    MoveColumnToWorkspace(#[proptest(strategy = \"0..=4usize\")] usize, bool),\n    MoveWorkspaceDown,\n    MoveWorkspaceUp,\n    MoveWorkspaceToIndex {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        ws_name: Option<usize>,\n        #[proptest(strategy = \"0..=4usize\")]\n        target_idx: usize,\n    },\n    MoveWorkspaceToMonitor {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        ws_name: Option<usize>,\n        #[proptest(strategy = \"0..=5usize\")]\n        output_id: usize,\n    },\n    SetWorkspaceName {\n        #[proptest(strategy = \"1..=5usize\")]\n        new_ws_name: usize,\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        ws_name: Option<usize>,\n    },\n    UnsetWorkspaceName {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        ws_name: Option<usize>,\n    },\n    MoveWindowToOutput {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        window_id: Option<usize>,\n        #[proptest(strategy = \"1..=5usize\")]\n        output_id: usize,\n        #[proptest(strategy = \"proptest::option::of(0..=4usize)\")]\n        target_ws_idx: Option<usize>,\n    },\n    MoveColumnToOutput {\n        #[proptest(strategy = \"1..=5usize\")]\n        output_id: usize,\n        #[proptest(strategy = \"proptest::option::of(0..=4usize)\")]\n        target_ws_idx: Option<usize>,\n        activate: bool,\n    },\n    SwitchPresetColumnWidth,\n    SwitchPresetColumnWidthBack,\n    SwitchPresetWindowWidth {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    SwitchPresetWindowWidthBack {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    SwitchPresetWindowHeight {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    SwitchPresetWindowHeightBack {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    MaximizeColumn,\n    MaximizeWindowToEdges {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    SetColumnWidth(#[proptest(strategy = \"arbitrary_size_change()\")] SizeChange),\n    SetWindowWidth {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n        #[proptest(strategy = \"arbitrary_size_change()\")]\n        change: SizeChange,\n    },\n    SetWindowHeight {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n        #[proptest(strategy = \"arbitrary_size_change()\")]\n        change: SizeChange,\n    },\n    ResetWindowHeight {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    ExpandColumnToAvailableWidth,\n    ToggleWindowFloating {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n    },\n    SetWindowFloating {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n        floating: bool,\n    },\n    FocusFloating,\n    FocusTiling,\n    SwitchFocusFloatingTiling,\n    MoveFloatingWindow {\n        #[proptest(strategy = \"proptest::option::of(1..=5usize)\")]\n        id: Option<usize>,\n        #[proptest(strategy = \"arbitrary_position_change()\")]\n        x: PositionChange,\n        #[proptest(strategy = \"arbitrary_position_change()\")]\n        y: PositionChange,\n        animate: bool,\n    },\n    SetParent {\n        #[proptest(strategy = \"1..=5usize\")]\n        id: usize,\n        #[proptest(strategy = \"prop::option::of(1..=5usize)\")]\n        new_parent_id: Option<usize>,\n    },\n    SetForcedSize {\n        #[proptest(strategy = \"1..=5usize\")]\n        id: usize,\n        #[proptest(strategy = \"proptest::option::of(arbitrary_size())\")]\n        size: Option<Size<i32, Logical>>,\n    },\n    Communicate(#[proptest(strategy = \"1..=5usize\")] usize),\n    Refresh {\n        is_active: bool,\n    },\n    AdvanceAnimations {\n        #[proptest(strategy = \"arbitrary_msec_delta()\")]\n        msec_delta: i32,\n    },\n    CompleteAnimations,\n    MoveWorkspaceToOutput(#[proptest(strategy = \"1..=5usize\")] usize),\n    ViewOffsetGestureBegin {\n        #[proptest(strategy = \"1..=5usize\")]\n        output_idx: usize,\n        #[proptest(strategy = \"proptest::option::of(0..=4usize)\")]\n        workspace_idx: Option<usize>,\n        is_touchpad: bool,\n    },\n    ViewOffsetGestureUpdate {\n        #[proptest(strategy = \"arbitrary_view_offset_gesture_delta()\")]\n        delta: f64,\n        timestamp: Duration,\n        is_touchpad: bool,\n    },\n    ViewOffsetGestureEnd {\n        is_touchpad: Option<bool>,\n    },\n    WorkspaceSwitchGestureBegin {\n        #[proptest(strategy = \"1..=5usize\")]\n        output_idx: usize,\n        is_touchpad: bool,\n    },\n    WorkspaceSwitchGestureUpdate {\n        #[proptest(strategy = \"-400f64..400f64\")]\n        delta: f64,\n        timestamp: Duration,\n        is_touchpad: bool,\n    },\n    WorkspaceSwitchGestureEnd {\n        is_touchpad: Option<bool>,\n    },\n    OverviewGestureBegin,\n    OverviewGestureUpdate {\n        #[proptest(strategy = \"-400f64..400f64\")]\n        delta: f64,\n        timestamp: Duration,\n    },\n    OverviewGestureEnd,\n    InteractiveMoveBegin {\n        #[proptest(strategy = \"1..=5usize\")]\n        window: usize,\n        #[proptest(strategy = \"1..=5usize\")]\n        output_idx: usize,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        px: f64,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        py: f64,\n    },\n    InteractiveMoveUpdate {\n        #[proptest(strategy = \"1..=5usize\")]\n        window: usize,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        dx: f64,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        dy: f64,\n        #[proptest(strategy = \"1..=5usize\")]\n        output_idx: usize,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        px: f64,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        py: f64,\n    },\n    InteractiveMoveEnd {\n        #[proptest(strategy = \"1..=5usize\")]\n        window: usize,\n    },\n    DndUpdate {\n        #[proptest(strategy = \"1..=5usize\")]\n        output_idx: usize,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        px: f64,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        py: f64,\n    },\n    DndEnd,\n    InteractiveResizeBegin {\n        #[proptest(strategy = \"1..=5usize\")]\n        window: usize,\n        #[proptest(strategy = \"arbitrary_resize_edge()\")]\n        edges: ResizeEdge,\n    },\n    InteractiveResizeUpdate {\n        #[proptest(strategy = \"1..=5usize\")]\n        window: usize,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        dx: f64,\n        #[proptest(strategy = \"-20000f64..20000f64\")]\n        dy: f64,\n    },\n    InteractiveResizeEnd {\n        #[proptest(strategy = \"1..=5usize\")]\n        window: usize,\n    },\n    ToggleOverview,\n    UpdateConfig {\n        #[proptest(strategy = \"arbitrary_layout_part().prop_map(Box::new)\")]\n        layout_config: Box<niri_config::LayoutPart>,\n    },\n}\n\nimpl Op {\n    fn apply(self, layout: &mut Layout<TestWindow>) {\n        match self {\n            Op::AddOutput(id) => {\n                let name = format!(\"output{id}\");\n                if layout.outputs().any(|o| o.name() == name) {\n                    return;\n                }\n\n                let output = Output::new(\n                    name.clone(),\n                    PhysicalProperties {\n                        size: Size::from((1280, 720)),\n                        subpixel: Subpixel::Unknown,\n                        make: String::new(),\n                        model: String::new(),\n                        serial_number: String::new(),\n                    },\n                );\n                output.change_current_state(\n                    Some(Mode {\n                        size: Size::from((1280, 720)),\n                        refresh: 60000,\n                    }),\n                    None,\n                    None,\n                    None,\n                );\n                output.user_data().insert_if_missing(|| OutputName {\n                    connector: name,\n                    make: None,\n                    model: None,\n                    serial: None,\n                });\n                layout.add_output(output.clone(), None);\n            }\n            Op::AddScaledOutput {\n                id,\n                scale,\n                layout_config,\n            } => {\n                let name = format!(\"output{id}\");\n                if layout.outputs().any(|o| o.name() == name) {\n                    return;\n                }\n\n                let output = Output::new(\n                    name.clone(),\n                    PhysicalProperties {\n                        size: Size::from((1280, 720)),\n                        subpixel: Subpixel::Unknown,\n                        make: String::new(),\n                        model: String::new(),\n                        serial_number: String::new(),\n                    },\n                );\n                output.change_current_state(\n                    Some(Mode {\n                        size: Size::from((1280, 720)),\n                        refresh: 60000,\n                    }),\n                    None,\n                    Some(smithay::output::Scale::Fractional(scale)),\n                    None,\n                );\n                output.user_data().insert_if_missing(|| OutputName {\n                    connector: name,\n                    make: None,\n                    model: None,\n                    serial: None,\n                });\n                layout.add_output(output.clone(), layout_config.map(|x| *x));\n            }\n            Op::RemoveOutput(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.remove_output(&output);\n            }\n            Op::FocusOutput(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.focus_output(&output);\n            }\n            Op::UpdateOutputLayoutConfig { id, layout_config } => {\n                let name = format!(\"output{id}\");\n                let Some(mon) = layout.monitors_mut().find(|m| m.output_name() == &name) else {\n                    return;\n                };\n\n                mon.update_layout_config(layout_config.map(|x| *x));\n            }\n            Op::AddNamedWorkspace {\n                ws_name,\n                output_name,\n                layout_config,\n            } => {\n                layout.ensure_named_workspace(&WorkspaceConfig {\n                    name: WorkspaceName(format!(\"ws{ws_name}\")),\n                    open_on_output: output_name.map(|name| format!(\"output{name}\")),\n                    layout: layout_config.map(|x| niri_config::WorkspaceLayoutPart(*x)),\n                });\n            }\n            Op::UnnameWorkspace { ws_name } => {\n                layout.unname_workspace(&format!(\"ws{ws_name}\"));\n            }\n            Op::UpdateWorkspaceLayoutConfig {\n                ws_name,\n                layout_config,\n            } => {\n                let ws_name = format!(\"ws{ws_name}\");\n                let Some(ws) = layout\n                    .workspaces_mut()\n                    .find(|ws| ws.name() == Some(&ws_name))\n                else {\n                    return;\n                };\n\n                ws.update_layout_config(layout_config.map(|x| *x));\n            }\n            Op::SetWorkspaceName {\n                new_ws_name,\n                ws_name,\n            } => {\n                let ws_ref =\n                    ws_name.map(|ws_name| WorkspaceReference::Name(format!(\"ws{ws_name}\")));\n                layout.set_workspace_name(format!(\"ws{new_ws_name}\"), ws_ref);\n            }\n            Op::UnsetWorkspaceName { ws_name } => {\n                let ws_ref =\n                    ws_name.map(|ws_name| WorkspaceReference::Name(format!(\"ws{ws_name}\")));\n                layout.unset_workspace_name(ws_ref);\n            }\n            Op::AddWindow { mut params } => {\n                if layout.has_window(&params.id) {\n                    return;\n                }\n                if let Some(parent_id) = params.parent_id {\n                    if parent_id_causes_loop(layout, params.id, parent_id) {\n                        params.parent_id = None;\n                    }\n                }\n\n                let is_floating = params.is_floating;\n                let win = TestWindow::new(params);\n                layout.add_window(\n                    win,\n                    AddWindowTarget::Auto,\n                    None,\n                    None,\n                    false,\n                    is_floating,\n                    ActivateWindow::default(),\n                );\n            }\n            Op::AddWindowNextTo {\n                mut params,\n                next_to_id,\n            } => {\n                let mut found_next_to = false;\n\n                if let Some(InteractiveMoveState::Moving(move_)) = &layout.interactive_move {\n                    let win_id = move_.tile.window().0.id;\n                    if win_id == params.id {\n                        return;\n                    }\n                    if win_id == next_to_id {\n                        found_next_to = true;\n                    }\n                }\n\n                match &mut layout.monitor_set {\n                    MonitorSet::Normal { monitors, .. } => {\n                        for mon in monitors {\n                            for ws in &mut mon.workspaces {\n                                for win in ws.windows() {\n                                    if win.0.id == params.id {\n                                        return;\n                                    }\n\n                                    if win.0.id == next_to_id {\n                                        found_next_to = true;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    MonitorSet::NoOutputs { workspaces, .. } => {\n                        for ws in workspaces {\n                            for win in ws.windows() {\n                                if win.0.id == params.id {\n                                    return;\n                                }\n\n                                if win.0.id == next_to_id {\n                                    found_next_to = true;\n                                }\n                            }\n                        }\n                    }\n                }\n\n                if !found_next_to {\n                    return;\n                }\n\n                if let Some(parent_id) = params.parent_id {\n                    if parent_id_causes_loop(layout, params.id, parent_id) {\n                        params.parent_id = None;\n                    }\n                }\n\n                let is_floating = params.is_floating;\n                let win = TestWindow::new(params);\n                layout.add_window(\n                    win,\n                    AddWindowTarget::NextTo(&next_to_id),\n                    None,\n                    None,\n                    false,\n                    is_floating,\n                    ActivateWindow::default(),\n                );\n            }\n            Op::AddWindowToNamedWorkspace {\n                mut params,\n                ws_name,\n            } => {\n                let ws_name = format!(\"ws{ws_name}\");\n                let mut ws_id = None;\n\n                if let Some(InteractiveMoveState::Moving(move_)) = &layout.interactive_move {\n                    if move_.tile.window().0.id == params.id {\n                        return;\n                    }\n                }\n\n                match &mut layout.monitor_set {\n                    MonitorSet::Normal { monitors, .. } => {\n                        for mon in monitors {\n                            for ws in &mut mon.workspaces {\n                                for win in ws.windows() {\n                                    if win.0.id == params.id {\n                                        return;\n                                    }\n                                }\n\n                                if ws\n                                    .name\n                                    .as_ref()\n                                    .is_some_and(|name| name.eq_ignore_ascii_case(&ws_name))\n                                {\n                                    ws_id = Some(ws.id());\n                                }\n                            }\n                        }\n                    }\n                    MonitorSet::NoOutputs { workspaces, .. } => {\n                        for ws in workspaces {\n                            for win in ws.windows() {\n                                if win.0.id == params.id {\n                                    return;\n                                }\n                            }\n\n                            if ws\n                                .name\n                                .as_ref()\n                                .is_some_and(|name| name.eq_ignore_ascii_case(&ws_name))\n                            {\n                                ws_id = Some(ws.id());\n                            }\n                        }\n                    }\n                }\n\n                let Some(ws_id) = ws_id else {\n                    return;\n                };\n\n                if let Some(parent_id) = params.parent_id {\n                    if parent_id_causes_loop(layout, params.id, parent_id) {\n                        params.parent_id = None;\n                    }\n                }\n\n                let is_floating = params.is_floating;\n                let win = TestWindow::new(params);\n                layout.add_window(\n                    win,\n                    AddWindowTarget::Workspace(ws_id),\n                    None,\n                    None,\n                    false,\n                    is_floating,\n                    ActivateWindow::default(),\n                );\n            }\n            Op::CloseWindow(id) => {\n                layout.remove_window(&id, Transaction::new());\n            }\n            Op::FullscreenWindow(id) => {\n                if !layout.has_window(&id) {\n                    return;\n                }\n                layout.toggle_fullscreen(&id);\n            }\n            Op::SetFullscreenWindow {\n                window,\n                is_fullscreen,\n            } => {\n                if !layout.has_window(&window) {\n                    return;\n                }\n                layout.set_fullscreen(&window, is_fullscreen);\n            }\n            Op::ToggleWindowedFullscreen(id) => {\n                if !layout.has_window(&id) {\n                    return;\n                }\n                layout.toggle_windowed_fullscreen(&id);\n            }\n            Op::FocusColumnLeft => layout.focus_left(),\n            Op::FocusColumnRight => layout.focus_right(),\n            Op::FocusColumnFirst => layout.focus_column_first(),\n            Op::FocusColumnLast => layout.focus_column_last(),\n            Op::FocusColumnRightOrFirst => layout.focus_column_right_or_first(),\n            Op::FocusColumnLeftOrLast => layout.focus_column_left_or_last(),\n            Op::FocusColumn(index) => layout.focus_column(index),\n            Op::FocusWindowOrMonitorUp(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.focus_window_up_or_output(&output);\n            }\n            Op::FocusWindowOrMonitorDown(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.focus_window_down_or_output(&output);\n            }\n            Op::FocusColumnOrMonitorLeft(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.focus_column_left_or_output(&output);\n            }\n            Op::FocusColumnOrMonitorRight(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.focus_column_right_or_output(&output);\n            }\n            Op::FocusWindowDown => layout.focus_down(),\n            Op::FocusWindowUp => layout.focus_up(),\n            Op::FocusWindowDownOrColumnLeft => layout.focus_down_or_left(),\n            Op::FocusWindowDownOrColumnRight => layout.focus_down_or_right(),\n            Op::FocusWindowUpOrColumnLeft => layout.focus_up_or_left(),\n            Op::FocusWindowUpOrColumnRight => layout.focus_up_or_right(),\n            Op::FocusWindowOrWorkspaceDown => layout.focus_window_or_workspace_down(),\n            Op::FocusWindowOrWorkspaceUp => layout.focus_window_or_workspace_up(),\n            Op::FocusWindow(id) => layout.activate_window(&id),\n            Op::FocusWindowInColumn(index) => layout.focus_window_in_column(index),\n            Op::FocusWindowTop => layout.focus_window_top(),\n            Op::FocusWindowBottom => layout.focus_window_bottom(),\n            Op::FocusWindowDownOrTop => layout.focus_window_down_or_top(),\n            Op::FocusWindowUpOrBottom => layout.focus_window_up_or_bottom(),\n            Op::MoveColumnLeft => layout.move_left(),\n            Op::MoveColumnRight => layout.move_right(),\n            Op::MoveColumnToFirst => layout.move_column_to_first(),\n            Op::MoveColumnToLast => layout.move_column_to_last(),\n            Op::MoveColumnLeftOrToMonitorLeft(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.move_column_left_or_to_output(&output);\n            }\n            Op::MoveColumnRightOrToMonitorRight(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.move_column_right_or_to_output(&output);\n            }\n            Op::MoveColumnToIndex(index) => layout.move_column_to_index(index),\n            Op::MoveWindowDown => layout.move_down(),\n            Op::MoveWindowUp => layout.move_up(),\n            Op::MoveWindowDownOrToWorkspaceDown => layout.move_down_or_to_workspace_down(),\n            Op::MoveWindowUpOrToWorkspaceUp => layout.move_up_or_to_workspace_up(),\n            Op::ConsumeOrExpelWindowLeft { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.consume_or_expel_window_left(id.as_ref());\n            }\n            Op::ConsumeOrExpelWindowRight { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.consume_or_expel_window_right(id.as_ref());\n            }\n            Op::ConsumeWindowIntoColumn => layout.consume_into_column(),\n            Op::ExpelWindowFromColumn => layout.expel_from_column(),\n            Op::SwapWindowInDirection(direction) => layout.swap_window_in_direction(direction),\n            Op::ToggleColumnTabbedDisplay => layout.toggle_column_tabbed_display(),\n            Op::SetColumnDisplay(display) => layout.set_column_display(display),\n            Op::CenterColumn => layout.center_column(),\n            Op::CenterWindow { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.center_window(id.as_ref());\n            }\n            Op::CenterVisibleColumns => layout.center_visible_columns(),\n            Op::FocusWorkspaceDown => layout.switch_workspace_down(),\n            Op::FocusWorkspaceUp => layout.switch_workspace_up(),\n            Op::FocusWorkspace(idx) => layout.switch_workspace(idx),\n            Op::FocusWorkspaceAutoBackAndForth(idx) => {\n                layout.switch_workspace_auto_back_and_forth(idx)\n            }\n            Op::FocusWorkspacePrevious => layout.switch_workspace_previous(),\n            Op::MoveWindowToWorkspaceDown(focus) => layout.move_to_workspace_down(focus),\n            Op::MoveWindowToWorkspaceUp(focus) => layout.move_to_workspace_up(focus),\n            Op::MoveWindowToWorkspace {\n                window_id,\n                workspace_idx,\n            } => {\n                let window_id = window_id.filter(|id| layout.has_window(id));\n                layout.move_to_workspace(window_id.as_ref(), workspace_idx, ActivateWindow::Smart);\n            }\n            Op::MoveColumnToWorkspaceDown(focus) => layout.move_column_to_workspace_down(focus),\n            Op::MoveColumnToWorkspaceUp(focus) => layout.move_column_to_workspace_up(focus),\n            Op::MoveColumnToWorkspace(idx, focus) => layout.move_column_to_workspace(idx, focus),\n            Op::MoveWindowToOutput {\n                window_id,\n                output_id: id,\n                target_ws_idx,\n            } => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n                let mon = layout.monitor_for_output(&output).unwrap();\n\n                let window_id = window_id.filter(|id| layout.has_window(id));\n                let target_ws_idx = target_ws_idx.filter(|idx| mon.workspaces.len() > *idx);\n                layout.move_to_output(\n                    window_id.as_ref(),\n                    &output,\n                    target_ws_idx,\n                    ActivateWindow::Smart,\n                );\n            }\n            Op::MoveColumnToOutput {\n                output_id: id,\n                target_ws_idx,\n                activate,\n            } => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.move_column_to_output(&output, target_ws_idx, activate);\n            }\n            Op::MoveWorkspaceDown => layout.move_workspace_down(),\n            Op::MoveWorkspaceUp => layout.move_workspace_up(),\n            Op::MoveWorkspaceToIndex {\n                ws_name: Some(ws_name),\n                target_idx,\n            } => {\n                let MonitorSet::Normal { monitors, .. } = &mut layout.monitor_set else {\n                    return;\n                };\n\n                let Some((old_idx, old_output)) = monitors.iter().find_map(|monitor| {\n                    monitor\n                        .workspaces\n                        .iter()\n                        .enumerate()\n                        .find_map(|(i, ws)| {\n                            if ws.name == Some(format!(\"ws{ws_name}\")) {\n                                Some(i)\n                            } else {\n                                None\n                            }\n                        })\n                        .map(|i| (i, monitor.output.clone()))\n                }) else {\n                    return;\n                };\n\n                layout.move_workspace_to_idx(Some((Some(old_output), old_idx)), target_idx)\n            }\n            Op::MoveWorkspaceToIndex {\n                ws_name: None,\n                target_idx,\n            } => layout.move_workspace_to_idx(None, target_idx),\n            Op::MoveWorkspaceToMonitor {\n                ws_name: None,\n                output_id: id,\n            } => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n                layout.move_workspace_to_output(&output);\n            }\n            Op::MoveWorkspaceToMonitor {\n                ws_name: Some(ws_name),\n                output_id: id,\n            } => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n                let MonitorSet::Normal { monitors, .. } = &mut layout.monitor_set else {\n                    return;\n                };\n\n                let Some((old_idx, old_output)) = monitors.iter().find_map(|monitor| {\n                    monitor\n                        .workspaces\n                        .iter()\n                        .enumerate()\n                        .find_map(|(i, ws)| {\n                            if ws.name == Some(format!(\"ws{ws_name}\")) {\n                                Some(i)\n                            } else {\n                                None\n                            }\n                        })\n                        .map(|i| (i, monitor.output.clone()))\n                }) else {\n                    return;\n                };\n\n                layout.move_workspace_to_output_by_id(old_idx, Some(old_output), &output);\n            }\n            Op::SwitchPresetColumnWidth => layout.toggle_width(true),\n            Op::SwitchPresetColumnWidthBack => layout.toggle_width(false),\n            Op::SwitchPresetWindowWidth { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.toggle_window_width(id.as_ref(), true);\n            }\n            Op::SwitchPresetWindowWidthBack { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.toggle_window_width(id.as_ref(), false);\n            }\n            Op::SwitchPresetWindowHeight { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.toggle_window_height(id.as_ref(), true);\n            }\n            Op::SwitchPresetWindowHeightBack { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.toggle_window_height(id.as_ref(), false);\n            }\n            Op::MaximizeColumn => layout.toggle_full_width(),\n            Op::MaximizeWindowToEdges { id } => {\n                let id = id.or_else(|| layout.focus().map(|win| *win.id()));\n                let Some(id) = id else {\n                    return;\n                };\n                if !layout.has_window(&id) {\n                    return;\n                }\n                layout.toggle_maximized(&id);\n            }\n            Op::SetColumnWidth(change) => layout.set_column_width(change),\n            Op::SetWindowWidth { id, change } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.set_window_width(id.as_ref(), change);\n            }\n            Op::SetWindowHeight { id, change } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.set_window_height(id.as_ref(), change);\n            }\n            Op::ResetWindowHeight { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.reset_window_height(id.as_ref());\n            }\n            Op::ExpandColumnToAvailableWidth => layout.expand_column_to_available_width(),\n            Op::ToggleWindowFloating { id } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.toggle_window_floating(id.as_ref());\n            }\n            Op::SetWindowFloating { id, floating } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.set_window_floating(id.as_ref(), floating);\n            }\n            Op::FocusFloating => {\n                layout.focus_floating();\n            }\n            Op::FocusTiling => {\n                layout.focus_tiling();\n            }\n            Op::SwitchFocusFloatingTiling => {\n                layout.switch_focus_floating_tiling();\n            }\n            Op::MoveFloatingWindow { id, x, y, animate } => {\n                let id = id.filter(|id| layout.has_window(id));\n                layout.move_floating_window(id.as_ref(), x, y, animate);\n            }\n            Op::SetParent {\n                id,\n                mut new_parent_id,\n            } => {\n                if !layout.has_window(&id) {\n                    return;\n                }\n\n                if let Some(parent_id) = new_parent_id {\n                    if parent_id_causes_loop(layout, id, parent_id) {\n                        new_parent_id = None;\n                    }\n                }\n\n                let mut update = false;\n\n                if let Some(InteractiveMoveState::Moving(move_)) = &layout.interactive_move {\n                    if move_.tile.window().0.id == id {\n                        move_.tile.window().0.parent_id.set(new_parent_id);\n                        update = true;\n                    }\n                }\n\n                match &mut layout.monitor_set {\n                    MonitorSet::Normal { monitors, .. } => {\n                        'outer: for mon in monitors {\n                            for ws in &mut mon.workspaces {\n                                for win in ws.windows() {\n                                    if win.0.id == id {\n                                        win.0.parent_id.set(new_parent_id);\n                                        update = true;\n                                        break 'outer;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    MonitorSet::NoOutputs { workspaces, .. } => {\n                        'outer: for ws in workspaces {\n                            for win in ws.windows() {\n                                if win.0.id == id {\n                                    win.0.parent_id.set(new_parent_id);\n                                    update = true;\n                                    break 'outer;\n                                }\n                            }\n                        }\n                    }\n                }\n\n                if update {\n                    if let Some(new_parent_id) = new_parent_id {\n                        layout.descendants_added(&new_parent_id);\n                    }\n                }\n            }\n            Op::SetForcedSize { id, size } => {\n                for (_mon, win) in layout.windows() {\n                    if win.0.id == id {\n                        win.0.forced_size.set(size);\n                        return;\n                    }\n                }\n            }\n            Op::Communicate(id) => {\n                let mut update = false;\n\n                if let Some(InteractiveMoveState::Moving(move_)) = &layout.interactive_move {\n                    if move_.tile.window().0.id == id {\n                        if move_.tile.window().communicate() {\n                            update = true;\n                        }\n\n                        if update {\n                            // FIXME: serial.\n                            layout.update_window(&id, None);\n                        }\n                        return;\n                    }\n                }\n\n                match &mut layout.monitor_set {\n                    MonitorSet::Normal { monitors, .. } => {\n                        'outer: for mon in monitors {\n                            for ws in &mut mon.workspaces {\n                                for win in ws.windows() {\n                                    if win.0.id == id {\n                                        if win.communicate() {\n                                            update = true;\n                                        }\n                                        break 'outer;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    MonitorSet::NoOutputs { workspaces, .. } => {\n                        'outer: for ws in workspaces {\n                            for win in ws.windows() {\n                                if win.0.id == id {\n                                    if win.communicate() {\n                                        update = true;\n                                    }\n                                    break 'outer;\n                                }\n                            }\n                        }\n                    }\n                }\n\n                if update {\n                    // FIXME: serial.\n                    layout.update_window(&id, None);\n                }\n            }\n            Op::Refresh { is_active } => {\n                layout.refresh(is_active);\n            }\n            Op::AdvanceAnimations { msec_delta } => {\n                let mut now = layout.clock.now_unadjusted();\n                if msec_delta >= 0 {\n                    now = now.saturating_add(Duration::from_millis(msec_delta as u64));\n                } else {\n                    now = now.saturating_sub(Duration::from_millis(-msec_delta as u64));\n                }\n                layout.clock.set_unadjusted(now);\n                layout.advance_animations();\n            }\n            Op::CompleteAnimations => {\n                layout.clock.set_complete_instantly(true);\n                layout.advance_animations();\n                layout.clock.set_complete_instantly(false);\n            }\n            Op::MoveWorkspaceToOutput(id) => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.move_workspace_to_output(&output);\n            }\n            Op::ViewOffsetGestureBegin {\n                output_idx: id,\n                workspace_idx,\n                is_touchpad: normalize,\n            } => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.view_offset_gesture_begin(&output, workspace_idx, normalize);\n            }\n            Op::ViewOffsetGestureUpdate {\n                delta,\n                timestamp,\n                is_touchpad,\n            } => {\n                layout.view_offset_gesture_update(delta, timestamp, is_touchpad);\n            }\n            Op::ViewOffsetGestureEnd { is_touchpad } => {\n                layout.view_offset_gesture_end(is_touchpad);\n            }\n            Op::WorkspaceSwitchGestureBegin {\n                output_idx: id,\n                is_touchpad,\n            } => {\n                let name = format!(\"output{id}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n\n                layout.workspace_switch_gesture_begin(&output, is_touchpad);\n            }\n            Op::WorkspaceSwitchGestureUpdate {\n                delta,\n                timestamp,\n                is_touchpad,\n            } => {\n                layout.workspace_switch_gesture_update(delta, timestamp, is_touchpad);\n            }\n            Op::WorkspaceSwitchGestureEnd { is_touchpad } => {\n                layout.workspace_switch_gesture_end(is_touchpad);\n            }\n            Op::OverviewGestureBegin => {\n                layout.overview_gesture_begin();\n            }\n            Op::OverviewGestureUpdate { delta, timestamp } => {\n                layout.overview_gesture_update(delta, timestamp);\n            }\n            Op::OverviewGestureEnd => {\n                layout.overview_gesture_end();\n            }\n            Op::InteractiveMoveBegin {\n                window,\n                output_idx,\n                px,\n                py,\n            } => {\n                let name = format!(\"output{output_idx}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n                layout.interactive_move_begin(window, &output, Point::from((px, py)));\n            }\n            Op::InteractiveMoveUpdate {\n                window,\n                dx,\n                dy,\n                output_idx,\n                px,\n                py,\n            } => {\n                let name = format!(\"output{output_idx}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n                layout.interactive_move_update(\n                    &window,\n                    Point::from((dx, dy)),\n                    output,\n                    Point::from((px, py)),\n                );\n            }\n            Op::InteractiveMoveEnd { window } => {\n                layout.interactive_move_end(&window);\n            }\n            Op::DndUpdate { output_idx, px, py } => {\n                let name = format!(\"output{output_idx}\");\n                let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {\n                    return;\n                };\n                layout.dnd_update(output, Point::from((px, py)));\n            }\n            Op::DndEnd => {\n                layout.dnd_end();\n            }\n            Op::InteractiveResizeBegin { window, edges } => {\n                layout.interactive_resize_begin(window, edges);\n            }\n            Op::InteractiveResizeUpdate { window, dx, dy } => {\n                layout.interactive_resize_update(&window, Point::from((dx, dy)));\n            }\n            Op::InteractiveResizeEnd { window } => {\n                layout.interactive_resize_end(&window);\n            }\n            Op::ToggleOverview => {\n                layout.toggle_overview();\n            }\n            Op::UpdateConfig { layout_config } => {\n                let options = Options {\n                    layout: niri_config::Layout::from_part(&layout_config),\n                    ..Default::default()\n                };\n\n                layout.update_options(options);\n            }\n        }\n    }\n}\n\n#[track_caller]\nfn check_ops_on_layout(layout: &mut Layout<TestWindow>, ops: impl IntoIterator<Item = Op>) {\n    for op in ops {\n        op.apply(layout);\n        layout.verify_invariants();\n    }\n}\n\n#[track_caller]\nfn check_ops(ops: impl IntoIterator<Item = Op>) -> Layout<TestWindow> {\n    let mut layout = Layout::default();\n    check_ops_on_layout(&mut layout, ops);\n    layout\n}\n\n#[track_caller]\nfn check_ops_with_options(\n    options: Options,\n    ops: impl IntoIterator<Item = Op>,\n) -> Layout<TestWindow> {\n    let mut layout = Layout::with_options(Clock::with_time(Duration::ZERO), options);\n    check_ops_on_layout(&mut layout, ops);\n    layout\n}\n\n#[test]\nfn operations_dont_panic() {\n    if std::env::var_os(\"RUN_SLOW_TESTS\").is_none() {\n        eprintln!(\"ignoring slow test\");\n        return;\n    }\n\n    let every_op = [\n        Op::AddOutput(0),\n        Op::AddOutput(1),\n        Op::AddOutput(2),\n        Op::RemoveOutput(0),\n        Op::RemoveOutput(1),\n        Op::RemoveOutput(2),\n        Op::FocusOutput(0),\n        Op::FocusOutput(1),\n        Op::FocusOutput(2),\n        Op::AddNamedWorkspace {\n            ws_name: 1,\n            output_name: Some(1),\n            layout_config: None,\n        },\n        Op::UnnameWorkspace { ws_name: 1 },\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindowNextTo {\n            params: TestWindowParams::new(2),\n            next_to_id: 1,\n        },\n        Op::AddWindowToNamedWorkspace {\n            params: TestWindowParams::new(3),\n            ws_name: 1,\n        },\n        Op::CloseWindow(0),\n        Op::CloseWindow(1),\n        Op::CloseWindow(2),\n        Op::FullscreenWindow(1),\n        Op::FullscreenWindow(2),\n        Op::FullscreenWindow(3),\n        Op::MaximizeWindowToEdges { id: Some(1) },\n        Op::MaximizeWindowToEdges { id: Some(2) },\n        Op::MaximizeWindowToEdges { id: Some(3) },\n        Op::FocusColumnLeft,\n        Op::FocusColumnRight,\n        Op::FocusColumnRightOrFirst,\n        Op::FocusColumnLeftOrLast,\n        Op::FocusWindowOrMonitorUp(0),\n        Op::FocusWindowOrMonitorDown(1),\n        Op::FocusColumnOrMonitorLeft(0),\n        Op::FocusColumnOrMonitorRight(1),\n        Op::FocusWindowUp,\n        Op::FocusWindowUpOrColumnLeft,\n        Op::FocusWindowUpOrColumnRight,\n        Op::FocusWindowOrWorkspaceUp,\n        Op::FocusWindowDown,\n        Op::FocusWindowDownOrColumnLeft,\n        Op::FocusWindowDownOrColumnRight,\n        Op::FocusWindowOrWorkspaceDown,\n        Op::MoveColumnLeft,\n        Op::MoveColumnRight,\n        Op::MoveColumnLeftOrToMonitorLeft(0),\n        Op::MoveColumnRightOrToMonitorRight(1),\n        Op::ConsumeWindowIntoColumn,\n        Op::ExpelWindowFromColumn,\n        Op::CenterColumn,\n        Op::FocusWorkspaceDown,\n        Op::FocusWorkspaceUp,\n        Op::FocusWorkspace(1),\n        Op::FocusWorkspace(2),\n        Op::MoveWindowToWorkspaceDown(true),\n        Op::MoveWindowToWorkspaceUp(true),\n        Op::MoveWindowToWorkspace {\n            window_id: None,\n            workspace_idx: 1,\n        },\n        Op::MoveWindowToWorkspace {\n            window_id: None,\n            workspace_idx: 2,\n        },\n        Op::MoveColumnToWorkspaceDown(true),\n        Op::MoveColumnToWorkspaceUp(true),\n        Op::MoveColumnToWorkspace(1, true),\n        Op::MoveColumnToWorkspace(2, true),\n        Op::MoveWindowDown,\n        Op::MoveWindowDownOrToWorkspaceDown,\n        Op::MoveWindowUp,\n        Op::MoveWindowUpOrToWorkspaceUp,\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::ConsumeOrExpelWindowRight { id: None },\n        Op::MoveWorkspaceToOutput(1),\n        Op::ToggleColumnTabbedDisplay,\n    ];\n\n    for third in &every_op {\n        for second in &every_op {\n            for first in &every_op {\n                // eprintln!(\"{first:?}, {second:?}, {third:?}\");\n\n                let mut layout = Layout::default();\n                first.clone().apply(&mut layout);\n                layout.verify_invariants();\n                second.clone().apply(&mut layout);\n                layout.verify_invariants();\n                third.clone().apply(&mut layout);\n                layout.verify_invariants();\n            }\n        }\n    }\n}\n\n#[test]\nfn operations_from_starting_state_dont_panic() {\n    if std::env::var_os(\"RUN_SLOW_TESTS\").is_none() {\n        eprintln!(\"ignoring slow test\");\n        return;\n    }\n\n    // Running every op from an empty state doesn't get us to all the interesting states. So,\n    // also run it from a manually-created starting state with more things going on to exercise\n    // more code paths.\n    let setup_ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::MoveWindowToWorkspaceDown(true),\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::FocusColumnLeft,\n        Op::ConsumeWindowIntoColumn,\n        Op::AddWindow {\n            params: TestWindowParams::new(4),\n        },\n        Op::AddOutput(2),\n        Op::AddWindow {\n            params: TestWindowParams::new(5),\n        },\n        Op::MoveWindowToOutput {\n            window_id: None,\n            output_id: 2,\n            target_ws_idx: None,\n        },\n        Op::FocusOutput(1),\n        Op::Communicate(1),\n        Op::Communicate(2),\n        Op::Communicate(3),\n        Op::Communicate(4),\n        Op::Communicate(5),\n    ];\n\n    let every_op = [\n        Op::AddOutput(0),\n        Op::AddOutput(1),\n        Op::AddOutput(2),\n        Op::RemoveOutput(0),\n        Op::RemoveOutput(1),\n        Op::RemoveOutput(2),\n        Op::FocusOutput(0),\n        Op::FocusOutput(1),\n        Op::FocusOutput(2),\n        Op::AddNamedWorkspace {\n            ws_name: 1,\n            output_name: Some(1),\n            layout_config: None,\n        },\n        Op::UnnameWorkspace { ws_name: 1 },\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddWindowNextTo {\n            params: TestWindowParams::new(6),\n            next_to_id: 0,\n        },\n        Op::AddWindowNextTo {\n            params: TestWindowParams::new(7),\n            next_to_id: 1,\n        },\n        Op::AddWindowToNamedWorkspace {\n            params: TestWindowParams::new(5),\n            ws_name: 1,\n        },\n        Op::CloseWindow(0),\n        Op::CloseWindow(1),\n        Op::CloseWindow(2),\n        Op::FullscreenWindow(1),\n        Op::FullscreenWindow(2),\n        Op::FullscreenWindow(3),\n        Op::MaximizeWindowToEdges { id: Some(1) },\n        Op::MaximizeWindowToEdges { id: Some(2) },\n        Op::MaximizeWindowToEdges { id: Some(3) },\n        Op::SetFullscreenWindow {\n            window: 1,\n            is_fullscreen: false,\n        },\n        Op::SetFullscreenWindow {\n            window: 1,\n            is_fullscreen: true,\n        },\n        Op::SetFullscreenWindow {\n            window: 2,\n            is_fullscreen: false,\n        },\n        Op::SetFullscreenWindow {\n            window: 2,\n            is_fullscreen: true,\n        },\n        Op::FocusColumnLeft,\n        Op::FocusColumnRight,\n        Op::FocusColumnRightOrFirst,\n        Op::FocusColumnLeftOrLast,\n        Op::FocusWindowOrMonitorUp(0),\n        Op::FocusWindowOrMonitorDown(1),\n        Op::FocusColumnOrMonitorLeft(0),\n        Op::FocusColumnOrMonitorRight(1),\n        Op::FocusWindowUp,\n        Op::FocusWindowUpOrColumnLeft,\n        Op::FocusWindowUpOrColumnRight,\n        Op::FocusWindowOrWorkspaceUp,\n        Op::FocusWindowDown,\n        Op::FocusWindowDownOrColumnLeft,\n        Op::FocusWindowDownOrColumnRight,\n        Op::FocusWindowOrWorkspaceDown,\n        Op::MoveColumnLeft,\n        Op::MoveColumnRight,\n        Op::MoveColumnLeftOrToMonitorLeft(0),\n        Op::MoveColumnRightOrToMonitorRight(1),\n        Op::ConsumeWindowIntoColumn,\n        Op::ExpelWindowFromColumn,\n        Op::CenterColumn,\n        Op::FocusWorkspaceDown,\n        Op::FocusWorkspaceUp,\n        Op::FocusWorkspace(1),\n        Op::FocusWorkspace(2),\n        Op::FocusWorkspace(3),\n        Op::MoveWindowToWorkspaceDown(true),\n        Op::MoveWindowToWorkspaceUp(true),\n        Op::MoveWindowToWorkspace {\n            window_id: None,\n            workspace_idx: 1,\n        },\n        Op::MoveWindowToWorkspace {\n            window_id: None,\n            workspace_idx: 2,\n        },\n        Op::MoveWindowToWorkspace {\n            window_id: None,\n            workspace_idx: 3,\n        },\n        Op::MoveColumnToWorkspaceDown(true),\n        Op::MoveColumnToWorkspaceUp(true),\n        Op::MoveColumnToWorkspace(1, true),\n        Op::MoveColumnToWorkspace(2, true),\n        Op::MoveColumnToWorkspace(3, true),\n        Op::MoveWindowDown,\n        Op::MoveWindowDownOrToWorkspaceDown,\n        Op::MoveWindowUp,\n        Op::MoveWindowUpOrToWorkspaceUp,\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::ConsumeOrExpelWindowRight { id: None },\n        Op::ToggleColumnTabbedDisplay,\n    ];\n\n    for third in &every_op {\n        for second in &every_op {\n            for first in &every_op {\n                // eprintln!(\"{first:?}, {second:?}, {third:?}\");\n\n                let mut layout = Layout::default();\n                for op in &setup_ops {\n                    op.clone().apply(&mut layout);\n                }\n\n                let mut layout = Layout::default();\n                first.clone().apply(&mut layout);\n                layout.verify_invariants();\n                second.clone().apply(&mut layout);\n                layout.verify_invariants();\n                third.clone().apply(&mut layout);\n                layout.verify_invariants();\n            }\n        }\n    }\n}\n\n#[test]\nfn primary_active_workspace_idx_not_updated_on_output_add() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddOutput(2),\n        Op::FocusOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FocusOutput(2),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::RemoveOutput(2),\n        Op::FocusWorkspace(3),\n        Op::AddOutput(2),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn window_closed_on_previous_workspace() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FocusWorkspaceDown,\n        Op::CloseWindow(0),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn removing_output_must_keep_empty_focus_on_primary() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddOutput(2),\n        Op::RemoveOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    // The workspace from the removed output was inserted at position 0, so the active workspace\n    // must change to 1 to keep the focus on the empty workspace.\n    assert_eq!(monitors[0].active_workspace_idx, 1);\n}\n\n#[test]\nfn move_to_workspace_by_idx_does_not_leave_empty_workspaces() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddOutput(2),\n        Op::FocusOutput(2),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::RemoveOutput(1),\n        Op::MoveWindowToWorkspace {\n            window_id: Some(0),\n            workspace_idx: 2,\n        },\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    assert!(monitors[0].workspaces[1].has_windows());\n}\n\n#[test]\nfn empty_workspaces_dont_move_back_to_original_output() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddOutput(2),\n        Op::RemoveOutput(1),\n        Op::FocusWorkspace(1),\n        Op::CloseWindow(1),\n        Op::AddOutput(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn named_workspaces_dont_update_original_output_on_adding_window() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::SetWorkspaceName {\n            new_ws_name: 1,\n            ws_name: None,\n        },\n        Op::AddOutput(2),\n        Op::RemoveOutput(1),\n        Op::FocusWorkspaceUp,\n        // Adding a window updates the original output for unnamed workspaces.\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        // Connecting the previous output should move the named workspace back since its\n        // original output wasn't updated.\n        Op::AddOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n    let (mon, _, ws) = layout\n        .workspaces()\n        .find(|(_, _, ws)| ws.name().is_some())\n        .unwrap();\n    assert!(ws.name().is_some()); // Sanity check.\n    let mon = mon.unwrap();\n    assert_eq!(mon.output_name(), \"output1\");\n}\n\n#[test]\nfn workspaces_update_original_output_on_moving_to_same_output() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::SetWorkspaceName {\n            new_ws_name: 1,\n            ws_name: None,\n        },\n        Op::AddOutput(2),\n        Op::RemoveOutput(1),\n        Op::FocusWorkspaceUp,\n        Op::MoveWorkspaceToOutput(2),\n        Op::AddOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n    let (mon, _, ws) = layout\n        .workspaces()\n        .find(|(_, _, ws)| ws.name().is_some())\n        .unwrap();\n    assert!(ws.name().is_some()); // Sanity check.\n    let mon = mon.unwrap();\n    assert_eq!(mon.output_name(), \"output2\");\n}\n\n#[test]\nfn workspaces_update_original_output_on_moving_to_same_monitor() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::SetWorkspaceName {\n            new_ws_name: 1,\n            ws_name: None,\n        },\n        Op::AddOutput(2),\n        Op::RemoveOutput(1),\n        Op::FocusWorkspaceUp,\n        Op::MoveWorkspaceToMonitor {\n            ws_name: Some(1),\n            output_id: 2,\n        },\n        Op::AddOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n    let (mon, _, ws) = layout\n        .workspaces()\n        .find(|(_, _, ws)| ws.name().is_some())\n        .unwrap();\n    assert!(ws.name().is_some()); // Sanity check.\n    let mon = mon.unwrap();\n    assert_eq!(mon.output_name(), \"output2\");\n}\n\n#[test]\nfn large_negative_height_change() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::AdjustProportion(-1e129),\n        },\n    ];\n\n    let mut options = Options::default();\n    options.layout.border.off = false;\n    options.layout.border.width = 1.;\n\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn large_max_size() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams {\n                min_max_size: (Size::from((0, 0)), Size::from((i32::MAX, i32::MAX))),\n                ..TestWindowParams::new(1)\n            },\n        },\n    ];\n\n    let mut options = Options::default();\n    options.layout.border.off = false;\n    options.layout.border.width = 1.;\n\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn workspace_cleanup_during_switch() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::CloseWindow(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn workspace_transfer_during_switch() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddOutput(2),\n        Op::FocusOutput(2),\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::RemoveOutput(1),\n        Op::FocusWorkspaceDown,\n        Op::FocusWorkspaceDown,\n        Op::AddOutput(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn workspace_transfer_during_switch_from_last() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddOutput(2),\n        Op::RemoveOutput(1),\n        Op::FocusWorkspaceUp,\n        Op::AddOutput(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn workspace_transfer_during_switch_gets_cleaned_up() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::RemoveOutput(1),\n        Op::AddOutput(2),\n        Op::MoveColumnToWorkspaceDown(true),\n        Op::MoveColumnToWorkspaceDown(true),\n        Op::AddOutput(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn move_workspace_to_output() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddOutput(2),\n        Op::FocusOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::MoveWorkspaceToOutput(2),\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal {\n        monitors,\n        active_monitor_idx,\n        ..\n    } = layout.monitor_set\n    else {\n        unreachable!()\n    };\n\n    assert_eq!(active_monitor_idx, 1);\n    assert_eq!(monitors[0].workspaces.len(), 1);\n    assert!(!monitors[0].workspaces[0].has_windows());\n    assert_eq!(monitors[1].active_workspace_idx, 0);\n    assert_eq!(monitors[1].workspaces.len(), 2);\n    assert!(monitors[1].workspaces[0].has_windows());\n}\n\n#[test]\nfn open_right_of_on_different_workspace() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddWindowNextTo {\n            params: TestWindowParams::new(3),\n            next_to_id: 1,\n        },\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    let mon = monitors.into_iter().next().unwrap();\n    assert_eq!(\n        mon.active_workspace_idx, 1,\n        \"the second workspace must remain active\"\n    );\n    assert_eq!(\n        mon.workspaces[0].scrolling().active_column_idx(),\n        1,\n        \"the new window must become active\"\n    );\n}\n\n#[test]\n// empty_workspace_above_first = true\nfn open_right_of_on_different_workspace_ewaf() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddWindowNextTo {\n            params: TestWindowParams::new(3),\n            next_to_id: 1,\n        },\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    let layout = check_ops_with_options(options, ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    let mon = monitors.into_iter().next().unwrap();\n    assert_eq!(\n        mon.active_workspace_idx, 2,\n        \"the second workspace must remain active\"\n    );\n    assert_eq!(\n        mon.workspaces[1].scrolling().active_column_idx(),\n        1,\n        \"the new window must become active\"\n    );\n}\n\n#[test]\nfn removing_all_outputs_preserves_empty_named_workspaces() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddNamedWorkspace {\n            ws_name: 1,\n            output_name: None,\n            layout_config: None,\n        },\n        Op::AddNamedWorkspace {\n            ws_name: 2,\n            output_name: None,\n            layout_config: None,\n        },\n        Op::RemoveOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::NoOutputs { workspaces } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    assert_eq!(workspaces.len(), 2);\n}\n\n#[test]\nfn config_change_updates_cached_sizes() {\n    let mut config = Config::default();\n    let border = &mut config.layout.border;\n    border.off = false;\n    border.width = 2.;\n\n    let mut layout = Layout::new(Clock::default(), &config);\n\n    Op::AddWindow {\n        params: TestWindowParams {\n            bbox: Rectangle::from_size(Size::from((1280, 200))),\n            ..TestWindowParams::new(1)\n        },\n    }\n    .apply(&mut layout);\n\n    config.layout.border.width = 4.;\n    layout.update_config(&config);\n\n    layout.verify_invariants();\n}\n\n#[test]\nfn preset_height_change_removes_preset() {\n    let mut config = Config::default();\n    config.layout.preset_window_heights = vec![PresetSize::Fixed(1), PresetSize::Fixed(2)];\n\n    let mut layout = Layout::new(Clock::default(), &config);\n\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SwitchPresetWindowHeight { id: None },\n        Op::SwitchPresetWindowHeight { id: None },\n    ];\n    for op in ops {\n        op.apply(&mut layout);\n    }\n\n    // Leave only one.\n    config.layout.preset_window_heights = vec![PresetSize::Fixed(1)];\n\n    layout.update_config(&config);\n\n    layout.verify_invariants();\n}\n\n#[test]\nfn set_window_height_recomputes_to_auto() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        Op::FocusWindowUp,\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn one_window_in_column_becomes_weight_1() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(100),\n        },\n        Op::Communicate(2),\n        Op::FocusWindowUp,\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(200),\n        },\n        Op::Communicate(1),\n        Op::CloseWindow(0),\n        Op::CloseWindow(1),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn fixed_height_takes_max_non_auto_into_account() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::SetWindowHeight {\n            id: Some(0),\n            change: SizeChange::SetFixed(704),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            border: niri_config::Border {\n                off: false,\n                width: 4.,\n                ..Default::default()\n            },\n            gaps: 0.,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn start_interactive_move_then_remove_window() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::InteractiveMoveBegin {\n            window: 0,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n        Op::CloseWindow(0),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_move_onto_empty_output() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::InteractiveMoveBegin {\n            window: 0,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n        Op::AddOutput(2),\n        Op::InteractiveMoveUpdate {\n            window: 0,\n            dx: 1000.,\n            dy: 0.,\n            output_idx: 2,\n            px: 0.,\n            py: 0.,\n        },\n        Op::InteractiveMoveEnd { window: 0 },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_move_onto_empty_output_ewaf() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::InteractiveMoveBegin {\n            window: 0,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n        Op::AddOutput(2),\n        Op::InteractiveMoveUpdate {\n            window: 0,\n            dx: 1000.,\n            dy: 0.,\n            output_idx: 2,\n            px: 0.,\n            py: 0.,\n        },\n        Op::InteractiveMoveEnd { window: 0 },\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn interactive_move_onto_last_workspace() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::InteractiveMoveBegin {\n            window: 0,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n        Op::InteractiveMoveUpdate {\n            window: 0,\n            dx: 1000.,\n            dy: 0.,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n        Op::FocusWorkspaceDown,\n        Op::AdvanceAnimations { msec_delta: 1000 },\n        Op::InteractiveMoveEnd { window: 0 },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_move_onto_first_empty_workspace() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::InteractiveMoveBegin {\n            window: 1,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n        Op::InteractiveMoveUpdate {\n            window: 1,\n            dx: 1000.,\n            dy: 0.,\n            output_idx: 1,\n            px: 0.,\n            py: 0.,\n        },\n        Op::FocusWorkspaceUp,\n        Op::AdvanceAnimations { msec_delta: 1000 },\n        Op::InteractiveMoveEnd { window: 1 },\n    ];\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn output_active_workspace_is_preserved() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::RemoveOutput(1),\n        Op::AddOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    assert_eq!(monitors[0].active_workspace_idx, 1);\n}\n\n#[test]\nfn output_active_workspace_is_preserved_with_other_outputs() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddOutput(2),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::RemoveOutput(1),\n        Op::AddOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    assert_eq!(monitors[1].active_workspace_idx, 1);\n}\n\n#[test]\nfn named_workspace_to_output() {\n    let ops = [\n        Op::AddNamedWorkspace {\n            ws_name: 1,\n            output_name: None,\n            layout_config: None,\n        },\n        Op::AddOutput(1),\n        Op::MoveWorkspaceToOutput(1),\n        Op::FocusWorkspaceUp,\n    ];\n    check_ops(ops);\n}\n\n#[test]\n// empty_workspace_above_first = true\nfn named_workspace_to_output_ewaf() {\n    let ops = [\n        Op::AddNamedWorkspace {\n            ws_name: 1,\n            output_name: Some(2),\n            layout_config: None,\n        },\n        Op::AddOutput(1),\n        Op::AddOutput(2),\n    ];\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn move_window_to_empty_workspace_above_first() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::MoveWorkspaceUp,\n        Op::MoveWorkspaceDown,\n        Op::FocusWorkspaceUp,\n        Op::MoveWorkspaceDown,\n    ];\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn move_window_to_different_output() {\n    let ops = [\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddOutput(1),\n        Op::AddOutput(2),\n        Op::MoveWorkspaceToOutput(2),\n    ];\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn close_window_empty_ws_above_first() {\n    let ops = [\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddOutput(1),\n        Op::CloseWindow(1),\n    ];\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn add_and_remove_output() {\n    let ops = [\n        Op::AddOutput(2),\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::RemoveOutput(2),\n    ];\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn switch_ewaf_on() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n    ];\n\n    let mut layout = check_ops(ops);\n    layout.update_options(Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    });\n    layout.verify_invariants();\n}\n\n#[test]\nfn switch_ewaf_off() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    let mut layout = check_ops_with_options(options, ops);\n    layout.update_options(Options::default());\n    layout.verify_invariants();\n}\n\n#[test]\nfn interactive_move_drop_on_other_output_during_animation() {\n    let ops = [\n        Op::AddOutput(3),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::InteractiveMoveBegin {\n            window: 3,\n            output_idx: 3,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::FocusWorkspaceDown,\n        Op::AddOutput(4),\n        Op::InteractiveMoveUpdate {\n            window: 3,\n            dx: 0.0,\n            dy: 8300.68619826683,\n            output_idx: 4,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::RemoveOutput(4),\n        Op::InteractiveMoveEnd { window: 3 },\n    ];\n    check_ops(ops);\n}\n\n#[test]\nfn add_window_next_to_only_interactively_moved_without_outputs() {\n    let ops = [\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddOutput(1),\n        Op::InteractiveMoveBegin {\n            window: 2,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::InteractiveMoveUpdate {\n            window: 2,\n            dx: 0.0,\n            dy: 3586.692842955048,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::RemoveOutput(1),\n        // We have no outputs, and the only existing window is interactively moved, meaning there\n        // are no workspaces either.\n        Op::AddWindowNextTo {\n            params: TestWindowParams::new(3),\n            next_to_id: 2,\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_move_toggle_floating_ends_dnd_gesture() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::InteractiveMoveBegin {\n            window: 2,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::InteractiveMoveUpdate {\n            window: 2,\n            dx: 0.0,\n            dy: 3586.692842955048,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::Refresh { is_active: false },\n        Op::ToggleWindowFloating { id: None },\n        Op::InteractiveMoveEnd { window: 2 },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_move_from_workspace_with_layout_config() {\n    let ops = [\n        Op::AddNamedWorkspace {\n            ws_name: 1,\n            output_name: Some(2),\n            layout_config: Some(Box::new(niri_config::LayoutPart {\n                border: Some(niri_config::BorderRule {\n                    on: true,\n                    ..Default::default()\n                }),\n                ..Default::default()\n            })),\n        },\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::InteractiveMoveBegin {\n            window: 2,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        Op::InteractiveMoveUpdate {\n            window: 2,\n            dx: 0.0,\n            dy: 3586.692842955048,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        // Now remove and add the output. It will have the same workspace.\n        Op::RemoveOutput(1),\n        Op::AddOutput(1),\n        Op::InteractiveMoveUpdate {\n            window: 2,\n            dx: 0.0,\n            dy: 0.0,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n        // Now move onto a different workspace.\n        Op::FocusWorkspaceDown,\n        Op::CompleteAnimations,\n        Op::InteractiveMoveUpdate {\n            window: 2,\n            dx: 0.0,\n            dy: 0.0,\n            output_idx: 1,\n            px: 0.0,\n            py: 0.0,\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn set_width_fixed_negative() {\n    let ops = [\n        Op::AddOutput(3),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::ToggleWindowFloating { id: Some(3) },\n        Op::SetColumnWidth(SizeChange::SetFixed(-100)),\n    ];\n    check_ops(ops);\n}\n\n#[test]\nfn set_height_fixed_negative() {\n    let ops = [\n        Op::AddOutput(3),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::ToggleWindowFloating { id: Some(3) },\n        Op::SetWindowHeight {\n            id: None,\n            change: SizeChange::SetFixed(-100),\n        },\n    ];\n    check_ops(ops);\n}\n\n#[test]\nfn interactive_resize_to_negative() {\n    let ops = [\n        Op::AddOutput(3),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::ToggleWindowFloating { id: Some(3) },\n        Op::InteractiveResizeBegin {\n            window: 3,\n            edges: ResizeEdge::BOTTOM_RIGHT,\n        },\n        Op::InteractiveResizeUpdate {\n            window: 3,\n            dx: -10000.,\n            dy: -10000.,\n        },\n    ];\n    check_ops(ops);\n}\n\n#[test]\nfn windows_on_other_workspaces_remain_activated() {\n    let ops = [\n        Op::AddOutput(3),\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::FocusWorkspaceDown,\n        Op::Refresh { is_active: true },\n    ];\n\n    let layout = check_ops(ops);\n    let (_, win) = layout.windows().next().unwrap();\n    assert!(win.0.pending_activated.get());\n}\n\n#[test]\nfn stacking_add_parent_brings_up_child() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                parent_id: Some(1),\n                ..TestWindowParams::new(0)\n            },\n        },\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                ..TestWindowParams::new(1)\n            },\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn stacking_add_parent_brings_up_descendants() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                parent_id: Some(2),\n                ..TestWindowParams::new(0)\n            },\n        },\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                parent_id: Some(0),\n                ..TestWindowParams::new(1)\n            },\n        },\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                ..TestWindowParams::new(2)\n            },\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn stacking_activate_brings_up_descendants() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                ..TestWindowParams::new(0)\n            },\n        },\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                parent_id: Some(0),\n                ..TestWindowParams::new(1)\n            },\n        },\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                parent_id: Some(1),\n                ..TestWindowParams::new(2)\n            },\n        },\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                ..TestWindowParams::new(3)\n            },\n        },\n        Op::FocusWindow(0),\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn stacking_set_parent_brings_up_child() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                ..TestWindowParams::new(0)\n            },\n        },\n        Op::AddWindow {\n            params: TestWindowParams {\n                is_floating: true,\n                ..TestWindowParams::new(1)\n            },\n        },\n        Op::SetParent {\n            id: 0,\n            new_parent_id: Some(1),\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn move_window_to_workspace_with_different_active_output() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FocusOutput(1),\n        Op::MoveWindowToWorkspace {\n            window_id: Some(0),\n            workspace_idx: 2,\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn set_first_workspace_name() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::SetWorkspaceName {\n            new_ws_name: 0,\n            ws_name: None,\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn set_first_workspace_name_ewaf() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::SetWorkspaceName {\n            new_ws_name: 0,\n            ws_name: None,\n        },\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            empty_workspace_above_first: true,\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn set_last_workspace_name() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FocusWorkspaceDown,\n        Op::SetWorkspaceName {\n            new_ws_name: 0,\n            ws_name: None,\n        },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn move_workspace_to_same_monitor_doesnt_reorder() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::SetWorkspaceName {\n            new_ws_name: 0,\n            ws_name: None,\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::FocusWorkspaceDown,\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::MoveWorkspaceToMonitor {\n            ws_name: Some(0),\n            output_id: 0,\n        },\n    ];\n\n    let layout = check_ops(ops);\n    let counts: Vec<_> = layout\n        .workspaces()\n        .map(|(_, _, ws)| ws.windows().count())\n        .collect();\n    assert_eq!(counts, &[1, 2, 0]);\n}\n\n#[test]\nfn removing_window_above_preserves_focused_window() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::FocusColumnFirst,\n        Op::ConsumeWindowIntoColumn,\n        Op::ConsumeWindowIntoColumn,\n        Op::FocusWindowDown,\n        Op::CloseWindow(0),\n    ];\n\n    let layout = check_ops(ops);\n    let win = layout.focus().unwrap();\n    assert_eq!(win.0.id, 1);\n}\n\n#[test]\nfn preset_column_width_fixed_correct_with_border() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::SwitchPresetColumnWidth,\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            preset_column_widths: vec![PresetSize::Fixed(500)],\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    let mut layout = check_ops_with_options(options, ops);\n\n    let win = layout.windows().next().unwrap().1;\n    assert_eq!(win.requested_size().unwrap().w, 500);\n\n    // Add border.\n    let options = Options {\n        layout: niri_config::Layout {\n            preset_column_widths: vec![PresetSize::Fixed(500)],\n            border: niri_config::Border {\n                off: false,\n                width: 5.,\n                ..Default::default()\n            },\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    layout.update_options(options);\n\n    // With border, the window gets less size.\n    let win = layout.windows().next().unwrap().1;\n    assert_eq!(win.requested_size().unwrap().w, 490);\n\n    // However, preset fixed width will still work correctly.\n    layout.toggle_width(true);\n    let win = layout.windows().next().unwrap().1;\n    assert_eq!(win.requested_size().unwrap().w, 500);\n}\n\n#[test]\nfn preset_column_width_reset_after_set_width() {\n    let ops = [\n        Op::AddOutput(0),\n        Op::AddWindow {\n            params: TestWindowParams::new(0),\n        },\n        Op::SwitchPresetColumnWidth,\n        Op::SetWindowWidth {\n            id: None,\n            change: SizeChange::AdjustFixed(-10),\n        },\n        Op::SwitchPresetColumnWidth,\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            preset_column_widths: vec![PresetSize::Fixed(500), PresetSize::Fixed(1000)],\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    let layout = check_ops_with_options(options, ops);\n    let win = layout.windows().next().unwrap().1;\n    assert_eq!(win.requested_size().unwrap().w, 500);\n}\n\n#[test]\nfn move_column_to_workspace_unfocused_with_multiple_monitors() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::SetWorkspaceName {\n            new_ws_name: 101,\n            ws_name: None,\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::SetWorkspaceName {\n            new_ws_name: 102,\n            ws_name: None,\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::AddOutput(2),\n        Op::FocusOutput(2),\n        Op::SetWorkspaceName {\n            new_ws_name: 201,\n            ws_name: None,\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(4),\n        },\n        Op::MoveColumnToOutput {\n            output_id: 1,\n            target_ws_idx: Some(0),\n            activate: false,\n        },\n        Op::FocusOutput(1),\n    ];\n\n    let layout = check_ops(ops);\n\n    assert_eq!(layout.active_workspace().unwrap().name().unwrap(), \"ws102\");\n\n    for (mon, win) in layout.windows() {\n        let mon = mon.unwrap();\n        let ws = mon\n            .workspaces\n            .iter()\n            .find(|w| w.has_window(win.id()))\n            .unwrap();\n\n        assert_eq!(\n            ws.name().unwrap(),\n            match win.id() {\n                1 | 4 => \"ws101\",\n                2 => \"ws102\",\n                3 => \"ws201\",\n                _ => unreachable!(),\n            }\n        );\n    }\n}\n\n#[test]\nfn move_column_to_workspace_down_focus_false_on_floating_window() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ToggleWindowFloating { id: None },\n        Op::MoveColumnToWorkspaceDown(false),\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    assert_eq!(monitors[0].active_workspace_idx, 0);\n}\n\n#[test]\nfn move_column_to_workspace_focus_false_on_floating_window() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ToggleWindowFloating { id: None },\n        Op::MoveColumnToWorkspace(1, false),\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = layout.monitor_set else {\n        unreachable!()\n    };\n\n    assert_eq!(monitors[0].active_workspace_idx, 0);\n}\n\n#[test]\nfn restore_to_floating_persists_across_fullscreen_maximize() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ToggleWindowFloating { id: None },\n        // Maximize then fullscreen.\n        Op::MaximizeWindowToEdges { id: None },\n        Op::FullscreenWindow(1),\n        // Unfullscreen.\n        Op::FullscreenWindow(1),\n    ];\n\n    let mut layout = check_ops(ops);\n\n    // Unfullscreening should return the window to the maximized state.\n    let scrolling = layout.active_workspace().unwrap().scrolling();\n    assert!(scrolling.tiles().next().is_some());\n\n    let ops = [\n        // Unmaximize.\n        Op::MaximizeWindowToEdges { id: None },\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Unmaximize should return the window back to floating.\n    let scrolling = layout.active_workspace().unwrap().scrolling();\n    assert!(scrolling.tiles().next().is_none());\n}\n\n#[test]\nfn unmaximize_during_fullscreen_does_not_float() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::ToggleWindowFloating { id: None },\n        // Maximize then fullscreen.\n        Op::MaximizeWindowToEdges { id: None },\n        Op::FullscreenWindow(1),\n        // Unmaximize.\n        Op::MaximizeWindowToEdges { id: None },\n    ];\n\n    let mut layout = check_ops(ops);\n\n    // Unmaximize shouldn't have changed the window state since it's fullscreen.\n    let scrolling = layout.active_workspace().unwrap().scrolling();\n    assert!(scrolling.tiles().next().is_some());\n\n    let ops = [\n        // Unfullscreen.\n        Op::FullscreenWindow(1),\n    ];\n    check_ops_on_layout(&mut layout, ops);\n\n    // Unfullscreen should return the window back to floating.\n    let scrolling = layout.active_workspace().unwrap().scrolling();\n    assert!(scrolling.tiles().next().is_none());\n}\n\n#[test]\nfn move_column_to_workspace_maximize_and_fullscreen() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::MaximizeWindowToEdges { id: None },\n        Op::FullscreenWindow(1),\n        Op::MoveColumnToWorkspaceDown(true),\n        Op::FullscreenWindow(1),\n    ];\n\n    let layout = check_ops(ops);\n    let (_, win) = layout.windows().next().unwrap();\n\n    // Unfullscreening should return to maximized because the window was maximized before.\n    assert_eq!(win.pending_sizing_mode(), SizingMode::Maximized);\n}\n\n#[test]\nfn move_window_to_workspace_maximize_and_fullscreen() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::MaximizeWindowToEdges { id: None },\n        Op::FullscreenWindow(1),\n        Op::MoveWindowToWorkspaceDown(true),\n        Op::FullscreenWindow(1),\n    ];\n\n    let layout = check_ops(ops);\n    let (_, win) = layout.windows().next().unwrap();\n\n    // Unfullscreening should return to maximized because the window was maximized before.\n    //\n    // FIXME: it currently doesn't because windows themselves can only be either fullscreen or\n    // maximized. So when a window is fullscreen, whether it is also maximized or not is stored in\n    // the column. MoveWindowToWorkspace removes the window from the column and this information is\n    // forgotten.\n    assert_eq!(win.pending_sizing_mode(), SizingMode::Normal);\n}\n\n#[test]\nfn tabs_with_different_border() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams {\n                rules: Some(ResolvedWindowRules {\n                    border: niri_config::BorderRule {\n                        on: true,\n                        ..Default::default()\n                    },\n                    ..ResolvedWindowRules::default()\n                }),\n                ..TestWindowParams::new(2)\n            },\n        },\n        Op::SwitchPresetWindowHeight { id: None },\n        Op::ToggleColumnTabbedDisplay,\n        Op::AddWindow {\n            params: TestWindowParams::new(3),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: None },\n    ];\n\n    let options = Options {\n        layout: niri_config::Layout {\n            struts: Struts {\n                left: FloatOrInt(0.),\n                right: FloatOrInt(0.),\n                top: FloatOrInt(20000.),\n                bottom: FloatOrInt(0.),\n            },\n            ..Default::default()\n        },\n        ..Default::default()\n    };\n    check_ops_with_options(options, ops);\n}\n\n#[test]\nfn expel_pending_left_from_fullscreen_tabbed_column() {\n    let ops = [\n        Op::AddOutput(1),\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FullscreenWindow(1),\n        Op::Communicate(1),\n        // 1 is now fullscreen, view_offset_to_restore is set.\n        Op::ToggleColumnTabbedDisplay,\n        Op::AddWindow {\n            params: TestWindowParams::new(2),\n        },\n        Op::ConsumeOrExpelWindowLeft { id: Some(2) },\n        // 2 is consumed into a fullscreen column, fullscreen is requested but not applied.\n        //\n        // Now, get it back out while keeping it focused.\n        //\n        // Importantly, we expel it *left*, which results in adding a new column with the exact\n        // same active_column_idx.\n        Op::FocusWindow(2),\n        Op::ConsumeOrExpelWindowLeft { id: None },\n    ];\n\n    check_ops(ops);\n}\n\n#[test]\nfn workspace_render_geo_at_fractional_scale() {\n    let ops = [\n        Op::AddScaledOutput {\n            id: 1,\n            scale: 1.1,\n            layout_config: None,\n        },\n        Op::AddWindow {\n            params: TestWindowParams::new(1),\n        },\n        Op::FocusWorkspaceDown,\n        Op::CompleteAnimations,\n    ];\n\n    let layout = check_ops(ops);\n\n    let MonitorSet::Normal { monitors, .. } = &layout.monitor_set else {\n        unreachable!()\n    };\n\n    let mon = &monitors[0];\n    let mut iter = mon.workspaces_with_render_geo();\n    let (_ws, geo) = iter.next().unwrap();\n    assert!(\n        iter.next().is_none(),\n        \"animations are completed, only one workspace should be visible\"\n    );\n    assert_eq!(\n        geo.loc.y, 0.,\n        \"active workspace must be at y = 0 exactly, \\\n         otherwise a pointer against the screen edge at y = 0 won't hit it\"\n    );\n}\n\nfn parent_id_causes_loop(layout: &Layout<TestWindow>, id: usize, mut parent_id: usize) -> bool {\n    if parent_id == id {\n        return true;\n    }\n\n    'outer: loop {\n        for (_, win) in layout.windows() {\n            if win.0.id == parent_id {\n                match win.0.parent_id.get() {\n                    Some(new_parent_id) => {\n                        if new_parent_id == id {\n                            // Found a loop.\n                            return true;\n                        }\n\n                        parent_id = new_parent_id;\n                        continue 'outer;\n                    }\n                    // Reached window with no parent.\n                    None => return false,\n                }\n            }\n        }\n\n        // Parent is not in the layout.\n        return false;\n    }\n}\n\nfn arbitrary_spacing() -> impl Strategy<Value = f64> {\n    // Give equal weight to:\n    // - 0: the element is disabled\n    // - 4: some reasonable value\n    // - random value, likely unreasonably big\n    prop_oneof![Just(0.), Just(4.), ((1.)..=65535.)]\n}\n\nfn arbitrary_spacing_neg() -> impl Strategy<Value = f64> {\n    // Give equal weight to:\n    // - 0: the element is disabled\n    // - 4: some reasonable value\n    // - -4: some reasonable negative value\n    // - random value, likely unreasonably big\n    prop_oneof![Just(0.), Just(4.), Just(-4.), ((1.)..=65535.)]\n}\n\nfn arbitrary_struts() -> impl Strategy<Value = Struts> {\n    (\n        arbitrary_spacing_neg(),\n        arbitrary_spacing_neg(),\n        arbitrary_spacing_neg(),\n        arbitrary_spacing_neg(),\n    )\n        .prop_map(|(left, right, top, bottom)| Struts {\n            left: FloatOrInt(left),\n            right: FloatOrInt(right),\n            top: FloatOrInt(top),\n            bottom: FloatOrInt(bottom),\n        })\n}\n\nfn arbitrary_center_focused_column() -> impl Strategy<Value = CenterFocusedColumn> {\n    prop_oneof![\n        Just(CenterFocusedColumn::Never),\n        Just(CenterFocusedColumn::OnOverflow),\n        Just(CenterFocusedColumn::Always),\n    ]\n}\n\nfn arbitrary_tab_indicator_position() -> impl Strategy<Value = TabIndicatorPosition> {\n    prop_oneof![\n        Just(TabIndicatorPosition::Left),\n        Just(TabIndicatorPosition::Right),\n        Just(TabIndicatorPosition::Top),\n        Just(TabIndicatorPosition::Bottom),\n    ]\n}\n\nprop_compose! {\n    fn arbitrary_focus_ring()(\n        off in any::<bool>(),\n        width in prop::option::of(arbitrary_spacing().prop_map(FloatOrInt)),\n    ) -> niri_config::BorderRule {\n        niri_config::BorderRule {\n            off,\n            on: !off,\n            width,\n            ..Default::default()\n        }\n    }\n}\n\nprop_compose! {\n    fn arbitrary_border()(\n        off in any::<bool>(),\n        width in prop::option::of(arbitrary_spacing().prop_map(FloatOrInt)),\n    ) -> niri_config::BorderRule {\n        niri_config::BorderRule {\n            off,\n            on: !off,\n            width,\n            ..Default::default()\n        }\n    }\n}\n\nprop_compose! {\n    fn arbitrary_shadow()(\n        off in any::<bool>(),\n        softness in prop::option::of(arbitrary_spacing().prop_map(FloatOrInt)),\n    ) -> niri_config::ShadowRule {\n        niri_config::ShadowRule {\n            off,\n            on: !off,\n            softness,\n            ..Default::default()\n        }\n    }\n}\n\nprop_compose! {\n    fn arbitrary_tab_indicator()(\n        off in any::<bool>(),\n        hide_when_single_tab in prop::option::of(any::<bool>().prop_map(Flag)),\n        place_within_column in prop::option::of(any::<bool>().prop_map(Flag)),\n        width in prop::option::of(arbitrary_spacing().prop_map(FloatOrInt)),\n        gap in prop::option::of(arbitrary_spacing_neg().prop_map(FloatOrInt)),\n        length in prop::option::of((0f64..2f64)\n            .prop_map(|x| TabIndicatorLength { total_proportion: Some(x) })),\n        position in prop::option::of(arbitrary_tab_indicator_position()),\n    ) -> niri_config::TabIndicatorPart {\n        niri_config::TabIndicatorPart {\n            off,\n            on: !off,\n            hide_when_single_tab,\n            place_within_column,\n            width,\n            gap,\n            length,\n            position,\n            ..Default::default()\n        }\n    }\n}\n\nprop_compose! {\n    fn arbitrary_layout_part()(\n        gaps in prop::option::of(arbitrary_spacing().prop_map(FloatOrInt)),\n        struts in prop::option::of(arbitrary_struts()),\n        focus_ring in prop::option::of(arbitrary_focus_ring()),\n        border in prop::option::of(arbitrary_border()),\n        shadow in prop::option::of(arbitrary_shadow()),\n        tab_indicator in prop::option::of(arbitrary_tab_indicator()),\n        center_focused_column in prop::option::of(arbitrary_center_focused_column()),\n        always_center_single_column in prop::option::of(any::<bool>().prop_map(Flag)),\n        empty_workspace_above_first in prop::option::of(any::<bool>().prop_map(Flag)),\n    ) -> niri_config::LayoutPart {\n        niri_config::LayoutPart {\n            gaps,\n            struts,\n            center_focused_column,\n            always_center_single_column,\n            empty_workspace_above_first,\n            focus_ring,\n            border,\n            shadow,\n            tab_indicator,\n            ..Default::default()\n        }\n    }\n}\n\nproptest! {\n    #![proptest_config(ProptestConfig {\n        cases: if std::env::var_os(\"RUN_SLOW_TESTS\").is_none() {\n            eprintln!(\"ignoring slow test\");\n            0\n        } else {\n            ProptestConfig::default().cases\n        },\n        ..ProptestConfig::default()\n    })]\n\n    #[test]\n    fn random_operations_dont_panic(\n        ops: Vec<Op>,\n        layout_config in arbitrary_layout_part(),\n    ) {\n        // eprintln!(\"{ops:?}\");\n        let options = Options {\n            layout: niri_config::Layout::from_part(&layout_config),\n            ..Default::default()\n        };\n\n        check_ops_with_options(options, ops);\n    }\n}\n"
  },
  {
    "path": "src/layout/tile.rs",
    "content": "use core::f64;\nuse std::rc::Rc;\n\nuse niri_config::utils::MergeWith as _;\nuse niri_config::{Color, CornerRadius, GradientInterpolation};\nuse niri_ipc::WindowLayout;\nuse smithay::backend::renderer::element::{Element, Kind};\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Size};\n\nuse super::focus_ring::{FocusRing, FocusRingRenderElement};\nuse super::opening_window::{OpenAnimation, OpeningWindowRenderElement};\nuse super::shadow::Shadow;\nuse super::{\n    HitType, LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot, Options,\n    SizeFrac, RESIZE_ANIMATION_THRESHOLD,\n};\nuse crate::animation::{Animation, Clock};\nuse crate::layout::SizingMode;\nuse crate::niri_render_elements;\nuse crate::render_helpers::border::BorderRenderElement;\nuse crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage};\nuse crate::render_helpers::damage::ExtraDamage;\nuse crate::render_helpers::offscreen::{OffscreenBuffer, OffscreenRenderElement};\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::resize::ResizeRenderElement;\nuse crate::render_helpers::shadow::ShadowRenderElement;\nuse crate::render_helpers::snapshot::RenderSnapshot;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::RenderTarget;\nuse crate::utils::transaction::Transaction;\nuse crate::utils::{\n    baba_is_float_offset, round_logical_in_physical, round_logical_in_physical_max1,\n};\n\n/// Toplevel window with decorations.\n#[derive(Debug)]\npub struct Tile<W: LayoutElement> {\n    /// The toplevel window itself.\n    window: W,\n\n    /// The border around the window.\n    border: FocusRing,\n\n    /// The focus ring around the window.\n    focus_ring: FocusRing,\n\n    /// The shadow around the window.\n    shadow: Shadow,\n\n    /// This tile's current sizing mode.\n    ///\n    /// This will update only when the `window` actually goes maximized or fullscreen, rather than\n    /// right away, to avoid black backdrop flicker before the window has had a chance to resize.\n    sizing_mode: SizingMode,\n\n    /// The black backdrop for fullscreen windows.\n    fullscreen_backdrop: SolidColorBuffer,\n\n    /// Whether the tile should float upon unfullscreening.\n    pub(super) restore_to_floating: bool,\n\n    /// The size that the window should assume when going floating.\n    ///\n    /// This is generally the last size the window had when it was floating. It can be unknown if\n    /// the window starts out in the tiling layout or fullscreen.\n    pub(super) floating_window_size: Option<Size<i32, Logical>>,\n\n    /// The position that the tile should assume when going floating, relative to the floating\n    /// space working area.\n    ///\n    /// This is generally the last position the tile had when it was floating. It can be unknown if\n    /// the window starts out in the tiling layout.\n    pub(super) floating_pos: Option<Point<f64, SizeFrac>>,\n\n    /// Currently selected preset width index when this tile is floating.\n    pub(super) floating_preset_width_idx: Option<usize>,\n\n    /// Currently selected preset height index when this tile is floating.\n    pub(super) floating_preset_height_idx: Option<usize>,\n\n    /// The animation upon opening a window.\n    open_animation: Option<OpenAnimation>,\n\n    /// The animation of the window resizing.\n    resize_animation: Option<ResizeAnimation>,\n\n    /// The animation of a tile visually moving horizontally.\n    move_x_animation: Option<MoveAnimation>,\n\n    /// The animation of a tile visually moving vertically.\n    move_y_animation: Option<MoveAnimation>,\n\n    /// The animation of the tile's opacity.\n    pub(super) alpha_animation: Option<AlphaAnimation>,\n\n    /// Offset during the initial interactive move rubberband.\n    pub(super) interactive_move_offset: Point<f64, Logical>,\n\n    /// Snapshot of the last render for use in the close animation.\n    unmap_snapshot: Option<TileRenderSnapshot>,\n\n    /// Extra damage for clipped surface corner radius changes.\n    rounded_corner_damage: RoundedCornerDamage,\n\n    /// The view size for the tile's workspace.\n    ///\n    /// Used as the fullscreen target size.\n    view_size: Size<f64, Logical>,\n\n    /// Scale of the output the tile is on (and rounds its sizes to).\n    scale: f64,\n\n    /// Clock for driving animations.\n    pub(super) clock: Clock,\n\n    /// Configurable properties of the layout.\n    pub(super) options: Rc<Options>,\n}\n\nniri_render_elements! {\n    TileRenderElement<R> => {\n        LayoutElement = LayoutElementRenderElement<R>,\n        FocusRing = FocusRingRenderElement,\n        SolidColor = SolidColorRenderElement,\n        Opening = OpeningWindowRenderElement,\n        Resize = ResizeRenderElement,\n        Border = BorderRenderElement,\n        Shadow = ShadowRenderElement,\n        ClippedSurface = ClippedSurfaceRenderElement<R>,\n        Offscreen = OffscreenRenderElement,\n        ExtraDamage = ExtraDamage,\n    }\n}\n\npub type TileRenderSnapshot =\n    RenderSnapshot<TileRenderElement<GlesRenderer>, TileRenderElement<GlesRenderer>>;\n\n#[derive(Debug)]\nstruct ResizeAnimation {\n    anim: Animation,\n    size_from: Size<f64, Logical>,\n    snapshot: LayoutElementRenderSnapshot,\n    offscreen: OffscreenBuffer,\n    tile_size_from: Size<f64, Logical>,\n    // If the resize involved the fullscreen state at some point, this is the progress toward the\n    // fullscreen state. Used for things like fullscreen backdrop alpha.\n    //\n    // Note that this can be set even if this specific resize is between two non-fullscreen states,\n    // for example when issuing a new resize during an unfullscreen resize.\n    fullscreen_progress: Option<Animation>,\n    // Similar to above but for fullscreen-or-maximized.\n    expanded_progress: Option<Animation>,\n}\n\n#[derive(Debug)]\nstruct MoveAnimation {\n    anim: Animation,\n    from: f64,\n}\n\n#[derive(Debug)]\npub(super) struct AlphaAnimation {\n    pub(super) anim: Animation,\n    /// Whether the animation should persist after it's done.\n    ///\n    /// This is used by things like interactive move which need to animate alpha to\n    /// semitransparent, then hold it at semitransparent for a while, until the operation\n    /// completes.\n    pub(super) hold_after_done: bool,\n    offscreen: OffscreenBuffer,\n}\n\nimpl<W: LayoutElement> Tile<W> {\n    pub fn new(\n        window: W,\n        view_size: Size<f64, Logical>,\n        scale: f64,\n        clock: Clock,\n        options: Rc<Options>,\n    ) -> Self {\n        let rules = window.rules();\n        let border_config = options.layout.border.merged_with(&rules.border);\n        let focus_ring_config = options.layout.focus_ring.merged_with(&rules.focus_ring);\n        let shadow_config = options.layout.shadow.merged_with(&rules.shadow);\n        let sizing_mode = window.sizing_mode();\n\n        Self {\n            window,\n            border: FocusRing::new(border_config.into()),\n            focus_ring: FocusRing::new(focus_ring_config),\n            shadow: Shadow::new(shadow_config),\n            sizing_mode,\n            fullscreen_backdrop: SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.]),\n            restore_to_floating: false,\n            floating_window_size: None,\n            floating_pos: None,\n            floating_preset_width_idx: None,\n            floating_preset_height_idx: None,\n            open_animation: None,\n            resize_animation: None,\n            move_x_animation: None,\n            move_y_animation: None,\n            alpha_animation: None,\n            interactive_move_offset: Point::from((0., 0.)),\n            unmap_snapshot: None,\n            rounded_corner_damage: Default::default(),\n            view_size,\n            scale,\n            clock,\n            options,\n        }\n    }\n\n    pub fn update_config(\n        &mut self,\n        view_size: Size<f64, Logical>,\n        scale: f64,\n        options: Rc<Options>,\n    ) {\n        // If preset widths or heights changed, clear our stored preset index.\n        if self.options.layout.preset_column_widths != options.layout.preset_column_widths {\n            self.floating_preset_width_idx = None;\n        }\n        if self.options.layout.preset_window_heights != options.layout.preset_window_heights {\n            self.floating_preset_height_idx = None;\n        }\n\n        self.view_size = view_size;\n        self.scale = scale;\n        self.options = options;\n\n        let round_max1 = |logical| round_logical_in_physical_max1(self.scale, logical);\n\n        let rules = self.window.rules();\n\n        let mut border_config = self.options.layout.border.merged_with(&rules.border);\n        border_config.width = round_max1(border_config.width);\n        self.border.update_config(border_config.into());\n\n        let mut focus_ring_config = self\n            .options\n            .layout\n            .focus_ring\n            .merged_with(&rules.focus_ring);\n        focus_ring_config.width = round_max1(focus_ring_config.width);\n        self.focus_ring.update_config(focus_ring_config);\n\n        let shadow_config = self.options.layout.shadow.merged_with(&rules.shadow);\n        self.shadow.update_config(shadow_config);\n    }\n\n    pub fn update_shaders(&mut self) {\n        self.border.update_shaders();\n        self.focus_ring.update_shaders();\n        self.shadow.update_shaders();\n    }\n\n    pub fn update_window(&mut self) {\n        let prev_sizing_mode = self.sizing_mode;\n        self.sizing_mode = self.window.sizing_mode();\n\n        if let Some(animate_from) = self.window.take_animation_snapshot() {\n            let params = if let Some(resize) = self.resize_animation.take() {\n                // Compute like in animated_window_size(), but using the snapshot geometry (since\n                // the current one is already overwritten).\n                let mut size = animate_from.size;\n\n                let val = resize.anim.value();\n                let size_from = resize.size_from;\n                let tile_size_from = resize.tile_size_from;\n\n                size.w = size_from.w + (size.w - size_from.w) * val;\n                size.h = size_from.h + (size.h - size_from.h) * val;\n\n                let mut tile_size = animate_from.size;\n                if prev_sizing_mode.is_fullscreen() {\n                    tile_size.w = f64::max(tile_size.w, self.view_size.w);\n                    tile_size.h = f64::max(tile_size.h, self.view_size.h);\n                } else if prev_sizing_mode.is_normal() && !self.border.is_off() {\n                    let width = self.border.width();\n                    tile_size.w += width * 2.;\n                    tile_size.h += width * 2.;\n                }\n\n                tile_size.w = tile_size_from.w + (tile_size.w - tile_size_from.w) * val;\n                tile_size.h = tile_size_from.h + (tile_size.h - tile_size_from.h) * val;\n\n                let fullscreen_from = resize\n                    .fullscreen_progress\n                    .map(|anim| anim.clamped_value().clamp(0., 1.))\n                    .unwrap_or(if prev_sizing_mode.is_fullscreen() {\n                        1.\n                    } else {\n                        0.\n                    });\n\n                let expanded_from = resize\n                    .expanded_progress\n                    .map(|anim| anim.clamped_value().clamp(0., 1.))\n                    .unwrap_or(if prev_sizing_mode.is_normal() { 0. } else { 1. });\n\n                // Also try to reuse the existing offscreen buffer if we have one.\n                (\n                    size,\n                    tile_size,\n                    fullscreen_from,\n                    expanded_from,\n                    resize.offscreen,\n                )\n            } else {\n                let size = animate_from.size;\n\n                // Compute like in tile_size().\n                let mut tile_size = size;\n                if prev_sizing_mode.is_fullscreen() {\n                    tile_size.w = f64::max(tile_size.w, self.view_size.w);\n                    tile_size.h = f64::max(tile_size.h, self.view_size.h);\n                } else if prev_sizing_mode.is_normal() && !self.border.is_off() {\n                    let width = self.border.width();\n                    tile_size.w += width * 2.;\n                    tile_size.h += width * 2.;\n                }\n\n                let fullscreen_from = if prev_sizing_mode.is_fullscreen() {\n                    1.\n                } else {\n                    0.\n                };\n\n                let expanded_from = if prev_sizing_mode.is_normal() { 0. } else { 1. };\n\n                (\n                    size,\n                    tile_size,\n                    fullscreen_from,\n                    expanded_from,\n                    OffscreenBuffer::default(),\n                )\n            };\n            let (size_from, tile_size_from, fullscreen_from, expanded_from, offscreen) = params;\n\n            let change = self.window.size().to_f64().to_point() - size_from.to_point();\n            let change = f64::max(change.x.abs(), change.y.abs());\n            let tile_change = self.tile_size().to_f64().to_point() - tile_size_from.to_point();\n            let tile_change = f64::max(tile_change.x.abs(), tile_change.y.abs());\n            let change = f64::max(change, tile_change);\n            if change > RESIZE_ANIMATION_THRESHOLD {\n                let anim = Animation::new(\n                    self.clock.clone(),\n                    0.,\n                    1.,\n                    0.,\n                    self.options.animations.window_resize.anim,\n                );\n\n                let fullscreen_to = if self.sizing_mode.is_fullscreen() {\n                    1.\n                } else {\n                    0.\n                };\n                let expanded_to = if self.sizing_mode.is_normal() { 0. } else { 1. };\n                let fullscreen_progress = (fullscreen_from != fullscreen_to)\n                    .then(|| anim.restarted(fullscreen_from, fullscreen_to, 0.));\n                let expanded_progress = (expanded_from != expanded_to)\n                    .then(|| anim.restarted(expanded_from, expanded_to, 0.));\n\n                self.resize_animation = Some(ResizeAnimation {\n                    anim,\n                    size_from,\n                    snapshot: animate_from,\n                    offscreen,\n                    tile_size_from,\n                    fullscreen_progress,\n                    expanded_progress,\n                });\n            } else {\n                self.resize_animation = None;\n            }\n        }\n\n        let round_max1 = |logical| round_logical_in_physical_max1(self.scale, logical);\n\n        let rules = self.window.rules();\n        let mut border_config = self.options.layout.border.merged_with(&rules.border);\n        border_config.width = round_max1(border_config.width);\n        self.border.update_config(border_config.into());\n\n        let mut focus_ring_config = self\n            .options\n            .layout\n            .focus_ring\n            .merged_with(&rules.focus_ring);\n        focus_ring_config.width = round_max1(focus_ring_config.width);\n        self.focus_ring.update_config(focus_ring_config);\n\n        let shadow_config = self.options.layout.shadow.merged_with(&rules.shadow);\n        self.shadow.update_config(shadow_config);\n\n        let window_size = self.window_size();\n        let radius = rules\n            .geometry_corner_radius\n            .unwrap_or_default()\n            .fit_to(window_size.w as f32, window_size.h as f32);\n        self.rounded_corner_damage.set_corner_radius(radius);\n        self.rounded_corner_damage.set_size(window_size);\n    }\n\n    pub fn advance_animations(&mut self) {\n        if let Some(open) = &mut self.open_animation {\n            if open.is_done() {\n                self.open_animation = None;\n            }\n        }\n\n        if let Some(resize) = &mut self.resize_animation {\n            if resize.anim.is_done() {\n                self.resize_animation = None;\n            }\n        }\n\n        if let Some(move_) = &mut self.move_x_animation {\n            if move_.anim.is_done() {\n                self.move_x_animation = None;\n            }\n        }\n        if let Some(move_) = &mut self.move_y_animation {\n            if move_.anim.is_done() {\n                self.move_y_animation = None;\n            }\n        }\n\n        if let Some(alpha) = &mut self.alpha_animation {\n            if !alpha.hold_after_done && alpha.anim.is_done() {\n                self.alpha_animation = None;\n            }\n        }\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        self.are_transitions_ongoing() || self.window.rules().baba_is_float == Some(true)\n    }\n\n    pub fn are_transitions_ongoing(&self) -> bool {\n        self.open_animation.is_some()\n            || self.resize_animation.is_some()\n            || self.move_x_animation.is_some()\n            || self.move_y_animation.is_some()\n            || self\n                .alpha_animation\n                .as_ref()\n                .is_some_and(|alpha| !alpha.anim.is_done())\n    }\n\n    pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {\n        let rules = self.window.rules();\n        let animated_tile_size = self.animated_tile_size();\n        let expanded_progress = self.expanded_progress();\n\n        let draw_border_with_background = rules\n            .draw_border_with_background\n            .unwrap_or_else(|| !self.window.has_ssd());\n        let border_width = self.visual_border_width().unwrap_or(0.);\n\n        // Do the inverse of tile_size() in order to handle the unfullscreen animation for windows\n        // that were smaller than the fullscreen size, and therefore their animated_window_size() is\n        // currently much smaller than the tile size.\n        let mut border_window_size = animated_tile_size;\n        border_window_size.w -= border_width * 2.;\n        border_window_size.h -= border_width * 2.;\n\n        let radius = rules\n            .geometry_corner_radius\n            .map_or(CornerRadius::default(), |radius| {\n                radius.expanded_by(border_width as f32)\n            })\n            .scaled_by(1. - expanded_progress as f32);\n        self.border.update_render_elements(\n            border_window_size,\n            is_active,\n            !draw_border_with_background,\n            self.window.is_urgent(),\n            Rectangle::new(\n                view_rect.loc - Point::from((border_width, border_width)),\n                view_rect.size,\n            ),\n            radius,\n            self.scale,\n            1. - expanded_progress as f32,\n        );\n\n        let radius = if self.visual_border_width().is_some() {\n            radius\n        } else {\n            rules\n                .geometry_corner_radius\n                .unwrap_or_default()\n                .scaled_by(1. - expanded_progress as f32)\n        };\n        self.shadow.update_render_elements(\n            animated_tile_size,\n            is_active,\n            radius,\n            self.scale,\n            1. - expanded_progress as f32,\n        );\n\n        let draw_focus_ring_with_background = if self.border.is_off() {\n            draw_border_with_background\n        } else {\n            false\n        };\n        let radius = radius.expanded_by(self.focus_ring.width() as f32);\n        self.focus_ring.update_render_elements(\n            animated_tile_size,\n            is_active,\n            !draw_focus_ring_with_background,\n            self.window.is_urgent(),\n            view_rect,\n            radius,\n            self.scale,\n            1. - expanded_progress as f32,\n        );\n\n        self.fullscreen_backdrop.resize(animated_tile_size);\n    }\n\n    pub fn scale(&self) -> f64 {\n        self.scale\n    }\n\n    pub fn render_offset(&self) -> Point<f64, Logical> {\n        let mut offset = Point::from((0., 0.));\n\n        if let Some(move_) = &self.move_x_animation {\n            offset.x += move_.from * move_.anim.value();\n        }\n        if let Some(move_) = &self.move_y_animation {\n            offset.y += move_.from * move_.anim.value();\n        }\n\n        offset += self.interactive_move_offset;\n\n        offset\n    }\n\n    pub fn start_open_animation(&mut self) {\n        self.open_animation = Some(OpenAnimation::new(Animation::new(\n            self.clock.clone(),\n            0.,\n            1.,\n            0.,\n            self.options.animations.window_open.anim,\n        )));\n    }\n\n    pub fn resize_animation(&self) -> Option<&Animation> {\n        self.resize_animation.as_ref().map(|resize| &resize.anim)\n    }\n\n    pub fn animate_move_from(&mut self, from: Point<f64, Logical>) {\n        self.animate_move_x_from(from.x);\n        self.animate_move_y_from(from.y);\n    }\n\n    pub fn animate_move_x_from(&mut self, from: f64) {\n        self.animate_move_x_from_with_config(from, self.options.animations.window_movement.0);\n    }\n\n    pub fn animate_move_x_from_with_config(&mut self, from: f64, config: niri_config::Animation) {\n        let current_offset = self.render_offset().x;\n\n        // Preserve the previous config if ongoing.\n        let anim = self.move_x_animation.take().map(|move_| move_.anim);\n        let anim = anim\n            .map(|anim| anim.restarted(1., 0., 0.))\n            .unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config));\n\n        self.move_x_animation = Some(MoveAnimation {\n            anim,\n            from: from + current_offset,\n        });\n    }\n\n    pub fn animate_move_y_from(&mut self, from: f64) {\n        self.animate_move_y_from_with_config(from, self.options.animations.window_movement.0);\n    }\n\n    pub fn animate_move_y_from_with_config(&mut self, from: f64, config: niri_config::Animation) {\n        let current_offset = self.render_offset().y;\n\n        // Preserve the previous config if ongoing.\n        let anim = self.move_y_animation.take().map(|move_| move_.anim);\n        let anim = anim\n            .map(|anim| anim.restarted(1., 0., 0.))\n            .unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config));\n\n        self.move_y_animation = Some(MoveAnimation {\n            anim,\n            from: from + current_offset,\n        });\n    }\n\n    pub fn offset_move_y_anim_current(&mut self, offset: f64) {\n        if let Some(move_) = self.move_y_animation.as_mut() {\n            // If the anim is almost done, there's little point trying to offset it; we can let\n            // things jump. If it turns out like a bad idea, we could restart the anim instead.\n            let value = move_.anim.value();\n            if value > 0.001 {\n                move_.from += offset / value;\n            }\n        }\n    }\n\n    pub fn stop_move_animations(&mut self) {\n        self.move_x_animation = None;\n        self.move_y_animation = None;\n    }\n\n    pub fn animate_alpha(&mut self, from: f64, to: f64, config: niri_config::Animation) {\n        let from = from.clamp(0., 1.);\n        let to = to.clamp(0., 1.);\n\n        let (current, offscreen) = if let Some(alpha) = self.alpha_animation.take() {\n            (alpha.anim.clamped_value(), alpha.offscreen)\n        } else {\n            (from, OffscreenBuffer::default())\n        };\n\n        self.alpha_animation = Some(AlphaAnimation {\n            anim: Animation::new(self.clock.clone(), current, to, 0., config),\n            hold_after_done: false,\n            offscreen,\n        });\n    }\n\n    pub fn ensure_alpha_animates_to_1(&mut self) {\n        if let Some(alpha) = &self.alpha_animation {\n            if alpha.anim.to() != 1. {\n                // Cancel animation instead of starting a new one because the user likely wants to\n                // see the tile right away.\n                self.alpha_animation = None;\n            }\n        }\n    }\n\n    pub fn hold_alpha_animation_after_done(&mut self) {\n        if let Some(alpha) = &mut self.alpha_animation {\n            alpha.hold_after_done = true;\n        }\n    }\n\n    pub fn window(&self) -> &W {\n        &self.window\n    }\n\n    pub fn window_mut(&mut self) -> &mut W {\n        &mut self.window\n    }\n\n    pub fn sizing_mode(&self) -> SizingMode {\n        self.sizing_mode\n    }\n\n    fn fullscreen_progress(&self) -> f64 {\n        if let Some(resize) = &self.resize_animation {\n            if let Some(anim) = &resize.fullscreen_progress {\n                return anim.clamped_value().clamp(0., 1.);\n            }\n        }\n\n        if self.sizing_mode.is_fullscreen() {\n            1.\n        } else {\n            0.\n        }\n    }\n\n    fn expanded_progress(&self) -> f64 {\n        if let Some(resize) = &self.resize_animation {\n            if let Some(anim) = &resize.expanded_progress {\n                return anim.clamped_value().clamp(0., 1.);\n            }\n        }\n\n        if self.sizing_mode.is_normal() {\n            0.\n        } else {\n            1.\n        }\n    }\n\n    /// Returns `None` if the border is hidden and `Some(width)` if it should be shown.\n    pub fn effective_border_width(&self) -> Option<f64> {\n        if !self.sizing_mode.is_normal() {\n            return None;\n        }\n\n        if self.border.is_off() {\n            return None;\n        }\n\n        Some(self.border.width())\n    }\n\n    fn visual_border_width(&self) -> Option<f64> {\n        if self.border.is_off() {\n            return None;\n        }\n\n        let expanded_progress = self.expanded_progress();\n\n        // Only hide the border when fully expanded to avoid jarring border appearance.\n        if expanded_progress == 1. {\n            return None;\n        }\n\n        // FIXME: would be cool to, like, gradually resize the border from full width to 0 during\n        // fullscreening, but the rest of the code isn't quite ready for that yet. It needs to\n        // handle things like computing intermediate tile size when an animated resize starts during\n        // an animated unfullscreen resize.\n        Some(self.border.width())\n    }\n\n    /// Returns the location of the window's visual geometry within this Tile.\n    pub fn window_loc(&self) -> Point<f64, Logical> {\n        let mut loc = Point::from((0., 0.));\n\n        let window_size = self.animated_window_size();\n        let target_size = self.animated_tile_size();\n\n        // Center the window within its tile.\n        //\n        // - Without borders, the sizes match, so this difference is zero.\n        // - Borders always match from all sides, so this difference is pre-rounded to physical.\n        // - In fullscreen, if the window is smaller than the tile, then it gets centered, otherwise\n        //   the tile size matches the window.\n        // - During animations, the window remains centered within the tile; this is important for\n        //   the to/from fullscreen animation.\n        loc.x += (target_size.w - window_size.w) / 2.;\n        loc.y += (target_size.h - window_size.h) / 2.;\n\n        // Round to physical pixels.\n        loc = loc\n            .to_physical_precise_round(self.scale)\n            .to_logical(self.scale);\n\n        loc\n    }\n\n    pub fn tile_size(&self) -> Size<f64, Logical> {\n        let mut size = self.window_size();\n\n        if self.sizing_mode.is_fullscreen() {\n            // Normally we'd just return the fullscreen size here, but this makes things a bit\n            // nicer if a fullscreen window is bigger than the fullscreen size for some reason.\n            size.w = f64::max(size.w, self.view_size.w);\n            size.h = f64::max(size.h, self.view_size.h);\n            return size;\n        }\n\n        if let Some(width) = self.effective_border_width() {\n            size.w += width * 2.;\n            size.h += width * 2.;\n        }\n\n        size\n    }\n\n    pub fn tile_expected_or_current_size(&self) -> Size<f64, Logical> {\n        let mut size = self.window_expected_or_current_size();\n\n        if self.sizing_mode.is_fullscreen() {\n            // Normally we'd just return the fullscreen size here, but this makes things a bit\n            // nicer if a fullscreen window is bigger than the fullscreen size for some reason.\n            size.w = f64::max(size.w, self.view_size.w);\n            size.h = f64::max(size.h, self.view_size.h);\n            return size;\n        }\n\n        if let Some(width) = self.effective_border_width() {\n            size.w += width * 2.;\n            size.h += width * 2.;\n        }\n\n        size\n    }\n\n    pub fn window_size(&self) -> Size<f64, Logical> {\n        let mut size = self.window.size().to_f64();\n        size = size\n            .to_physical_precise_round(self.scale)\n            .to_logical(self.scale);\n        size\n    }\n\n    pub fn window_expected_or_current_size(&self) -> Size<f64, Logical> {\n        let size = self.window.expected_size();\n        let mut size = size.unwrap_or_else(|| self.window.size()).to_f64();\n        size = size\n            .to_physical_precise_round(self.scale)\n            .to_logical(self.scale);\n        size\n    }\n\n    pub fn animated_window_size(&self) -> Size<f64, Logical> {\n        let mut size = self.window_size();\n\n        if let Some(resize) = &self.resize_animation {\n            let val = resize.anim.value();\n            let size_from = resize.size_from.to_f64();\n\n            size.w = f64::max(1., size_from.w + (size.w - size_from.w) * val);\n            size.h = f64::max(1., size_from.h + (size.h - size_from.h) * val);\n            size = size\n                .to_physical_precise_round(self.scale)\n                .to_logical(self.scale);\n        }\n\n        size\n    }\n\n    pub fn animated_tile_size(&self) -> Size<f64, Logical> {\n        let mut size = self.tile_size();\n\n        if let Some(resize) = &self.resize_animation {\n            let val = resize.anim.value();\n            let size_from = resize.tile_size_from.to_f64();\n\n            size.w = f64::max(1., size_from.w + (size.w - size_from.w) * val);\n            size.h = f64::max(1., size_from.h + (size.h - size_from.h) * val);\n            size = size\n                .to_physical_precise_round(self.scale)\n                .to_logical(self.scale);\n        }\n\n        size\n    }\n\n    pub fn buf_loc(&self) -> Point<f64, Logical> {\n        let mut loc = Point::from((0., 0.));\n        loc += self.window_loc();\n        loc += self.window.buf_loc().to_f64();\n        loc\n    }\n\n    /// Returns a partially-filled [`WindowLayout`].\n    ///\n    /// Only the sizing properties that a [`Tile`] can fill are filled.\n    pub fn ipc_layout_template(&self) -> WindowLayout {\n        WindowLayout {\n            pos_in_scrolling_layout: None,\n            tile_size: self.tile_size().into(),\n            window_size: self.window().size().into(),\n            tile_pos_in_workspace_view: None,\n            window_offset_in_tile: self.window_loc().into(),\n        }\n    }\n\n    fn is_in_input_region(&self, mut point: Point<f64, Logical>) -> bool {\n        point -= self.window_loc().to_f64();\n        self.window.is_in_input_region(point)\n    }\n\n    fn is_in_activation_region(&self, point: Point<f64, Logical>) -> bool {\n        let activation_region = Rectangle::from_size(self.tile_size());\n        activation_region.contains(point)\n    }\n\n    pub fn hit(&self, point: Point<f64, Logical>) -> Option<HitType> {\n        let offset = self.bob_offset();\n        let point = point - offset;\n\n        if self.is_in_input_region(point) {\n            let win_pos = self.buf_loc() + offset;\n            Some(HitType::Input { win_pos })\n        } else if self.is_in_activation_region(point) {\n            Some(HitType::Activate {\n                is_tab_indicator: false,\n            })\n        } else {\n            None\n        }\n    }\n\n    pub fn request_tile_size(\n        &mut self,\n        mut size: Size<f64, Logical>,\n        animate: bool,\n        transaction: Option<Transaction>,\n    ) {\n        // Can't go through effective_border_width() because we might be fullscreen.\n        if !self.border.is_off() {\n            let width = self.border.width();\n            size.w = f64::max(1., size.w - width * 2.);\n            size.h = f64::max(1., size.h - width * 2.);\n        }\n\n        // The size request has to be i32 unfortunately, due to Wayland. We floor here instead of\n        // round to avoid situations where proportionally-sized columns don't fit on the screen\n        // exactly.\n        self.window.request_size(\n            size.to_i32_floor(),\n            SizingMode::Normal,\n            animate,\n            transaction,\n        );\n    }\n\n    pub fn tile_width_for_window_width(&self, size: f64) -> f64 {\n        if self.border.is_off() {\n            size\n        } else {\n            size + self.border.width() * 2.\n        }\n    }\n\n    pub fn tile_height_for_window_height(&self, size: f64) -> f64 {\n        if self.border.is_off() {\n            size\n        } else {\n            size + self.border.width() * 2.\n        }\n    }\n\n    pub fn window_width_for_tile_width(&self, size: f64) -> f64 {\n        if self.border.is_off() {\n            size\n        } else {\n            size - self.border.width() * 2.\n        }\n    }\n\n    pub fn window_height_for_tile_height(&self, size: f64) -> f64 {\n        if self.border.is_off() {\n            size\n        } else {\n            size - self.border.width() * 2.\n        }\n    }\n\n    pub fn request_maximized(\n        &mut self,\n        size: Size<f64, Logical>,\n        animate: bool,\n        transaction: Option<Transaction>,\n    ) {\n        self.window.request_size(\n            size.to_i32_round(),\n            SizingMode::Maximized,\n            animate,\n            transaction,\n        );\n    }\n\n    pub fn request_fullscreen(&mut self, animate: bool, transaction: Option<Transaction>) {\n        self.window.request_size(\n            self.view_size.to_i32_round(),\n            SizingMode::Fullscreen,\n            animate,\n            transaction,\n        );\n    }\n\n    pub fn min_size_nonfullscreen(&self) -> Size<f64, Logical> {\n        let mut size = self.window.min_size().to_f64();\n\n        // Can't go through effective_border_width() because we might be fullscreen.\n        if !self.border.is_off() {\n            let width = self.border.width();\n\n            size.w = f64::max(1., size.w);\n            size.h = f64::max(1., size.h);\n\n            size.w += width * 2.;\n            size.h += width * 2.;\n        }\n\n        size\n    }\n\n    pub fn max_size_nonfullscreen(&self) -> Size<f64, Logical> {\n        let mut size = self.window.max_size().to_f64();\n\n        // Can't go through effective_border_width() because we might be fullscreen.\n        if !self.border.is_off() {\n            let width = self.border.width();\n\n            if size.w > 0. {\n                size.w += width * 2.;\n            }\n            if size.h > 0. {\n                size.h += width * 2.;\n            }\n        }\n\n        size\n    }\n\n    pub fn bob_offset(&self) -> Point<f64, Logical> {\n        if self.window.rules().baba_is_float != Some(true) {\n            return Point::from((0., 0.));\n        }\n\n        let y = baba_is_float_offset(self.clock.now(), self.view_size.h);\n        let y = round_logical_in_physical(self.scale, y);\n        Point::from((0., y))\n    }\n\n    fn render_inner<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        focus_ring: bool,\n        target: RenderTarget,\n        push: &mut dyn FnMut(TileRenderElement<R>),\n    ) {\n        let _span = tracy_client::span!(\"Tile::render_inner\");\n\n        let scale = Scale::from(self.scale);\n        let fullscreen_progress = self.fullscreen_progress();\n        let expanded_progress = self.expanded_progress();\n\n        let win_alpha = if self.window.is_ignoring_opacity_window_rule() {\n            1.\n        } else {\n            let alpha = self.window.rules().opacity.unwrap_or(1.).clamp(0., 1.);\n\n            // Interpolate towards alpha = 1. at fullscreen.\n            let p = fullscreen_progress as f32;\n            alpha * (1. - p) + 1. * p\n        };\n\n        // This is here rather than in render_offset() because render_offset() is currently assumed\n        // by the code to be temporary. So, for example, interactive move will try to \"grab\" the\n        // tile at its current render offset and reset the render offset to zero by cancelling the\n        // tile move animations. On the other hand, bob_offset() is not resettable, so adding it in\n        // render_offset() would cause obvious animation glitches.\n        //\n        // This isn't to say that adding it here is perfect; indeed, it kind of breaks view_rect\n        // passed to update_render_elements(). But, it works well enough for what it is.\n        let location = location + self.bob_offset();\n\n        let window_loc = self.window_loc();\n        let window_size = self.window_size();\n        let animated_window_size = self.animated_window_size();\n        let window_render_loc = location + window_loc;\n        let area = Rectangle::new(window_render_loc, animated_window_size);\n\n        let rules = self.window.rules();\n\n        // Clip to geometry including during the fullscreen animation to help with buggy clients\n        // that submit a full-sized buffer before acking the fullscreen state (Firefox).\n        let clip_to_geometry = fullscreen_progress < 1. && rules.clip_to_geometry == Some(true);\n        let radius = rules\n            .geometry_corner_radius\n            .unwrap_or_default()\n            .scaled_by(1. - expanded_progress as f32);\n\n        // Popups go on top, whether it's resize or not.\n        self.window.render_popups(\n            renderer,\n            window_render_loc,\n            scale,\n            win_alpha,\n            target,\n            &mut |elem| push(elem.into()),\n        );\n\n        // If we're resizing, try to render a shader, or a fallback.\n        let mut pushed_resize = false;\n        if let Some(resize) = &self.resize_animation {\n            if ResizeRenderElement::has_shader(renderer) {\n                let gles_renderer = renderer.as_gles_renderer();\n\n                if let Some(texture_from) = resize.snapshot.texture(gles_renderer, scale, target) {\n                    let mut window_elements = Vec::new();\n                    self.window.render_normal(\n                        gles_renderer,\n                        Point::from((0., 0.)),\n                        scale,\n                        1.,\n                        target,\n                        &mut |elem| window_elements.push(elem),\n                    );\n\n                    let current = resize\n                        .offscreen\n                        .render(gles_renderer, scale, &window_elements)\n                        .map_err(|err| warn!(\"error rendering window to texture: {err:?}\"))\n                        .ok();\n\n                    // Clip blocked-out resizes unconditionally because they use solid color render\n                    // elements.\n                    let clip_to_geometry = if target\n                        .should_block_out(resize.snapshot.block_out_from)\n                        && target.should_block_out(rules.block_out_from)\n                    {\n                        true\n                    } else {\n                        clip_to_geometry\n                    };\n\n                    if let Some((elem_current, _sync_point, mut data)) = current {\n                        let texture_current = elem_current.texture().clone();\n                        // The offset and size are computed in physical pixels and converted to\n                        // logical with the same `scale`, so converting them back with rounding\n                        // inside the geometry() call gives us the same physical result back.\n                        let texture_current_geo = elem_current.geometry(scale);\n\n                        let elem = ResizeRenderElement::new(\n                            area,\n                            scale,\n                            texture_from.clone(),\n                            resize.snapshot.size,\n                            (texture_current, texture_current_geo),\n                            window_size,\n                            resize.anim.value() as f32,\n                            resize.anim.clamped_value().clamp(0., 1.) as f32,\n                            radius,\n                            clip_to_geometry,\n                            win_alpha,\n                        );\n\n                        // We're drawing the resize shader, not the offscreen directly.\n                        data.id = elem.id().clone();\n\n                        // This is not a problem for split popups as the code will look for them by\n                        // original id when it doesn't find them on the offscreen.\n                        self.window.set_offscreen_data(Some(data));\n                        push(elem.into());\n                        pushed_resize = true;\n                    }\n                }\n            }\n\n            if !pushed_resize {\n                let fallback_buffer = SolidColorBuffer::new(area.size, [1., 0., 0., 1.]);\n                let elem = SolidColorRenderElement::from_buffer(\n                    &fallback_buffer,\n                    area.loc,\n                    win_alpha,\n                    Kind::Unspecified,\n                );\n                push(elem.into());\n                pushed_resize = true;\n            }\n        }\n\n        // If we're not resizing, render the window itself.\n        let has_border_shader = BorderRenderElement::has_shader(renderer);\n        if !pushed_resize {\n            let geo = Rectangle::new(window_render_loc, window_size);\n            let radius = radius.fit_to(window_size.w as f32, window_size.h as f32);\n\n            let clip_shader = ClippedSurfaceRenderElement::shader(renderer).cloned();\n            let clip = |elem| match elem {\n                LayoutElementRenderElement::Wayland(elem) => {\n                    // If we should clip to geometry, render a clipped window.\n                    if clip_to_geometry {\n                        if let Some(shader) = clip_shader.clone() {\n                            if ClippedSurfaceRenderElement::will_clip(&elem, scale, geo, radius) {\n                                return ClippedSurfaceRenderElement::new(\n                                    elem,\n                                    scale,\n                                    geo,\n                                    shader.clone(),\n                                    radius,\n                                )\n                                .into();\n                            }\n                        }\n                    }\n\n                    // Otherwise, render it normally.\n                    LayoutElementRenderElement::Wayland(elem).into()\n                }\n                LayoutElementRenderElement::SolidColor(elem) => {\n                    // In this branch we're rendering a blocked-out window with a solid\n                    // color. We need to render it with a rounded corner shader even if\n                    // clip_to_geometry is false, because in this case we're assuming that\n                    // the unclipped window CSD already has corners rounded to the\n                    // user-provided radius, so our blocked-out rendering should match that\n                    // radius.\n                    if radius != CornerRadius::default() && has_border_shader {\n                        return BorderRenderElement::new(\n                            geo.size,\n                            Rectangle::from_size(geo.size),\n                            GradientInterpolation::default(),\n                            Color::from_color32f(elem.color()),\n                            Color::from_color32f(elem.color()),\n                            0.,\n                            Rectangle::from_size(geo.size),\n                            0.,\n                            radius,\n                            scale.x as f32,\n                            1.,\n                        )\n                        .with_location(geo.loc)\n                        .into();\n                    }\n\n                    // Otherwise, render the solid color as is.\n                    LayoutElementRenderElement::SolidColor(elem).into()\n                }\n            };\n\n            if clip_to_geometry && clip_shader.is_some() {\n                let damage = self.rounded_corner_damage.element();\n                push(damage.with_location(window_render_loc).into());\n            }\n\n            self.window.render_normal(\n                renderer,\n                window_render_loc,\n                scale,\n                win_alpha,\n                target,\n                &mut |elem| push(clip(elem)),\n            );\n        }\n\n        if fullscreen_progress > 0. {\n            let alpha = fullscreen_progress as f32;\n\n            // During the un/fullscreen animation, render a border element in order to use the\n            // animated corner radius.\n            if fullscreen_progress < 1. && has_border_shader {\n                let border_width = self.visual_border_width().unwrap_or(0.);\n                let radius = rules\n                    .geometry_corner_radius\n                    .map_or(CornerRadius::default(), |radius| {\n                        radius.expanded_by(border_width as f32)\n                    })\n                    .scaled_by(1. - expanded_progress as f32);\n\n                let size = self.fullscreen_backdrop.size();\n                let color = self.fullscreen_backdrop.color();\n                let elem = BorderRenderElement::new(\n                    size,\n                    Rectangle::from_size(size),\n                    GradientInterpolation::default(),\n                    Color::from_color32f(color),\n                    Color::from_color32f(color),\n                    0.,\n                    Rectangle::from_size(size),\n                    0.,\n                    radius,\n                    scale.x as f32,\n                    alpha,\n                )\n                .with_location(location);\n                push(elem.into());\n            } else {\n                let elem = SolidColorRenderElement::from_buffer(\n                    &self.fullscreen_backdrop,\n                    location,\n                    alpha,\n                    Kind::Unspecified,\n                );\n                push(elem.into());\n            }\n        }\n\n        if let Some(width) = self.visual_border_width() {\n            self.border.render(\n                renderer,\n                location + Point::from((width, width)),\n                &mut |elem| push(elem.into()),\n            );\n        }\n\n        // Hide the focus ring when maximized/fullscreened. It's not normally visible anyway due to\n        // being outside the monitor or obscured by a solid colored bar, but it is visible under\n        // semitransparent bars in maximized state (which is a bit weird) and in the overview (also\n        // a bit weird).\n        if focus_ring && expanded_progress < 1. {\n            self.focus_ring\n                .render(renderer, location, &mut |elem| push(elem.into()));\n        }\n\n        if expanded_progress < 1. {\n            self.shadow\n                .render(renderer, location, &mut |elem| push(elem.into()));\n        }\n    }\n\n    pub fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        focus_ring: bool,\n        target: RenderTarget,\n        push: &mut dyn FnMut(TileRenderElement<R>),\n    ) {\n        let _span = tracy_client::span!(\"Tile::render\");\n\n        let scale = Scale::from(self.scale);\n\n        let tile_alpha = self\n            .alpha_animation\n            .as_ref()\n            .map_or(1., |alpha| alpha.anim.clamped_value()) as f32;\n\n        let mut pushed = false;\n        self.window().set_offscreen_data(None);\n\n        if let Some(open) = &self.open_animation {\n            let renderer = renderer.as_gles_renderer();\n            let mut elements = Vec::new();\n            self.render_inner(\n                renderer,\n                Point::from((0., 0.)),\n                focus_ring,\n                target,\n                &mut |elem| elements.push(elem),\n            );\n            match open.render(\n                renderer,\n                &elements,\n                self.animated_tile_size(),\n                location,\n                scale,\n                tile_alpha,\n            ) {\n                Ok((elem, data)) => {\n                    self.window().set_offscreen_data(Some(data));\n                    push(elem.into());\n                    pushed = true;\n                }\n                Err(err) => {\n                    warn!(\"error rendering window opening animation: {err:?}\");\n                }\n            }\n        } else if let Some(alpha) = &self.alpha_animation {\n            let renderer = renderer.as_gles_renderer();\n            let mut elements = Vec::new();\n            self.render_inner(\n                renderer,\n                Point::from((0., 0.)),\n                focus_ring,\n                target,\n                &mut |elem| elements.push(elem),\n            );\n            match alpha.offscreen.render(renderer, scale, &elements) {\n                Ok((elem, _sync, data)) => {\n                    let offset = elem.offset();\n                    let elem = elem.with_alpha(tile_alpha).with_offset(location + offset);\n\n                    self.window().set_offscreen_data(Some(data));\n                    push(elem.into());\n                    pushed = true;\n                }\n                Err(err) => {\n                    warn!(\"error rendering tile to offscreen for alpha animation: {err:?}\");\n                }\n            }\n        }\n\n        if !pushed {\n            self.render_inner(renderer, location, focus_ring, target, &mut |elem| {\n                push(elem)\n            });\n        }\n    }\n\n    pub fn store_unmap_snapshot_if_empty(&mut self, renderer: &mut GlesRenderer) {\n        if self.unmap_snapshot.is_some() {\n            return;\n        }\n\n        self.unmap_snapshot = Some(self.render_snapshot(renderer));\n    }\n\n    fn render_snapshot(&self, renderer: &mut GlesRenderer) -> TileRenderSnapshot {\n        let _span = tracy_client::span!(\"Tile::render_snapshot\");\n\n        let mut contents = Vec::new();\n        self.render(\n            renderer,\n            Point::from((0., 0.)),\n            false,\n            RenderTarget::Output,\n            &mut |elem| contents.push(elem),\n        );\n\n        // A bit of a hack to render blocked out as for screencast, but I think it's fine here.\n        let mut blocked_out_contents = Vec::new();\n        self.render(\n            renderer,\n            Point::from((0., 0.)),\n            false,\n            RenderTarget::Screencast,\n            &mut |elem| blocked_out_contents.push(elem),\n        );\n\n        RenderSnapshot {\n            contents,\n            blocked_out_contents,\n            block_out_from: self.window.rules().block_out_from,\n            size: self.animated_tile_size(),\n            texture: Default::default(),\n            blocked_out_texture: Default::default(),\n        }\n    }\n\n    pub fn take_unmap_snapshot(&mut self) -> Option<TileRenderSnapshot> {\n        self.unmap_snapshot.take()\n    }\n\n    pub fn border(&self) -> &FocusRing {\n        &self.border\n    }\n\n    pub fn focus_ring(&self) -> &FocusRing {\n        &self.focus_ring\n    }\n\n    pub fn options(&self) -> &Rc<Options> {\n        &self.options\n    }\n\n    #[cfg(test)]\n    pub fn view_size(&self) -> Size<f64, Logical> {\n        self.view_size\n    }\n\n    #[cfg(test)]\n    pub fn verify_invariants(&self) {\n        use approx::assert_abs_diff_eq;\n\n        assert_eq!(self.sizing_mode, self.window.sizing_mode());\n\n        let scale = self.scale;\n        let size = self.tile_size();\n        let rounded = size.to_physical_precise_round(scale).to_logical(scale);\n        assert_abs_diff_eq!(size.w, rounded.w, epsilon = 1e-5);\n        assert_abs_diff_eq!(size.h, rounded.h, epsilon = 1e-5);\n    }\n}\n"
  },
  {
    "path": "src/layout/workspace.rs",
    "content": "use std::cmp::max;\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse niri_config::utils::MergeWith as _;\nuse niri_config::{\n    CenterFocusedColumn, CornerRadius, OutputName, PresetSize, Workspace as WorkspaceConfig,\n};\nuse niri_ipc::{ColumnDisplay, PositionChange, SizeChange, WindowLayout};\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::desktop::{layer_map_for_output, Window};\nuse smithay::output::Output;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::utils::{Logical, Point, Rectangle, Serial, Size, Transform};\nuse smithay::wayland::compositor::with_states;\nuse smithay::wayland::shell::xdg::SurfaceCachedState;\n\nuse super::floating::{FloatingSpace, FloatingSpaceRenderElement};\nuse super::scrolling::{\n    Column, ColumnWidth, ScrollDirection, ScrollingSpace, ScrollingSpaceRenderElement,\n};\nuse super::shadow::Shadow;\nuse super::tile::{Tile, TileRenderSnapshot};\nuse super::{\n    ActivateWindow, HitType, InsertPosition, InteractiveResizeData, LayoutElement, Options,\n    RemovedTile, SizeFrac,\n};\nuse crate::animation::Clock;\nuse crate::niri_render_elements;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::shadow::ShadowRenderElement;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::RenderTarget;\nuse crate::utils::id::IdCounter;\nuse crate::utils::transaction::{Transaction, TransactionBlocker};\nuse crate::utils::{\n    ensure_min_max_size, ensure_min_max_size_maybe_zero, output_size, send_scale_transform,\n    ResizeEdge,\n};\nuse crate::window::ResolvedWindowRules;\n\n#[derive(Debug)]\npub struct Workspace<W: LayoutElement> {\n    /// The scrollable-tiling layout.\n    scrolling: ScrollingSpace<W>,\n\n    /// The floating layout.\n    floating: FloatingSpace<W>,\n\n    /// Whether the floating layout is active instead of the scrolling layout.\n    floating_is_active: FloatingActive,\n\n    /// The original output of this workspace.\n    ///\n    /// Most of the time this will be the workspace's current output, however, after an output\n    /// disconnection, it may remain pointing to the disconnected output.\n    pub(super) original_output: OutputId,\n\n    /// Current output of this workspace.\n    output: Option<Output>,\n\n    /// Latest known output scale for this workspace.\n    ///\n    /// This should be set from the current workspace output, or, if all outputs have been\n    /// disconnected, preserved until a new output is connected.\n    scale: smithay::output::Scale,\n\n    /// Latest known output transform for this workspace.\n    ///\n    /// This should be set from the current workspace output, or, if all outputs have been\n    /// disconnected, preserved until a new output is connected.\n    transform: Transform,\n\n    /// Latest known view size for this workspace.\n    ///\n    /// This should be computed from the current workspace output size, or, if all outputs have\n    /// been disconnected, preserved until a new output is connected.\n    view_size: Size<f64, Logical>,\n\n    /// Latest known working area for this workspace.\n    ///\n    /// Not rounded to physical pixels.\n    ///\n    /// This is similar to view size, but takes into account things like layer shell exclusive\n    /// zones.\n    working_area: Rectangle<f64, Logical>,\n\n    /// This workspace's shadow in the overview.\n    shadow: Shadow,\n\n    /// This workspace's background.\n    background_buffer: SolidColorBuffer,\n\n    /// Clock for driving animations.\n    pub(super) clock: Clock,\n\n    /// Configurable properties of the layout as received from the parent monitor.\n    pub(super) base_options: Rc<Options>,\n\n    /// Configurable properties of the layout with logical sizes adjusted for the current `scale`.\n    pub(super) options: Rc<Options>,\n\n    /// Optional name of this workspace.\n    pub(super) name: Option<String>,\n\n    /// Layout config overrides for this workspace.\n    layout_config: Option<niri_config::LayoutPart>,\n\n    /// Unique ID of this workspace.\n    id: WorkspaceId,\n}\n\n#[derive(Debug, Clone)]\npub struct OutputId(String);\n\nimpl OutputId {\n    pub fn matches(&self, output: &Output) -> bool {\n        let output_name = output.user_data().get::<OutputName>().unwrap();\n        output_name.matches(&self.0)\n    }\n}\n\nstatic WORKSPACE_ID_COUNTER: IdCounter = IdCounter::new();\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct WorkspaceId(u64);\n\nimpl WorkspaceId {\n    fn next() -> WorkspaceId {\n        WorkspaceId(WORKSPACE_ID_COUNTER.next())\n    }\n\n    pub fn get(self) -> u64 {\n        self.0\n    }\n\n    pub fn specific(id: u64) -> Self {\n        Self(id)\n    }\n}\n\nniri_render_elements! {\n    WorkspaceRenderElement<R> => {\n        Scrolling = ScrollingSpaceRenderElement<R>,\n        Floating = FloatingSpaceRenderElement<R>,\n    }\n}\n\n#[derive(Debug)]\npub(super) struct InteractiveResize<W: LayoutElement> {\n    pub window: W::Id,\n    pub original_window_size: Size<f64, Logical>,\n    pub data: InteractiveResizeData,\n}\n\n/// Resolved width or height in logical pixels.\n#[derive(Debug, Clone, Copy)]\npub enum ResolvedSize {\n    /// Size of the tile including borders.\n    Tile(f64),\n    /// Size of the window excluding borders.\n    Window(f64),\n}\n\n/// Whether the floating space is active.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum FloatingActive {\n    /// The scrolling space is active.\n    No,\n    /// The scrolling space is active, but the floating space should render on top, even if the\n    /// active scrolling window is fullscreen.\n    ///\n    /// This is necessary for focus-follows-mouse that activates but doesn't raise the window to\n    /// avoid being annoying.\n    NoButRaised,\n    /// The floating space is active.\n    Yes,\n}\n\n/// Where to put a newly added window.\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub enum WorkspaceAddWindowTarget<'a, W: LayoutElement> {\n    /// No particular preference.\n    #[default]\n    Auto,\n    /// As a new column at this index.\n    NewColumnAt(usize),\n    /// Next to this existing window.\n    NextTo(&'a W::Id),\n}\n\nimpl OutputId {\n    pub fn new(output: &Output) -> Self {\n        let output_name = output.user_data().get::<OutputName>().unwrap();\n        Self(output_name.format_make_model_serial_or_connector())\n    }\n}\n\nimpl FloatingActive {\n    fn get(self) -> bool {\n        self == Self::Yes\n    }\n}\n\nimpl<W: LayoutElement> Workspace<W> {\n    pub fn new(output: Output, clock: Clock, options: Rc<Options>) -> Self {\n        Self::new_with_config(output, None, clock, options)\n    }\n\n    pub fn new_with_config(\n        output: Output,\n        mut config: Option<WorkspaceConfig>,\n        clock: Clock,\n        base_options: Rc<Options>,\n    ) -> Self {\n        let original_output = config\n            .as_ref()\n            .and_then(|c| c.open_on_output.clone())\n            .map(OutputId)\n            .unwrap_or(OutputId::new(&output));\n\n        let layout_config = config.as_mut().and_then(|c| c.layout.take().map(|x| x.0));\n\n        let scale = output.current_scale();\n        let options = Rc::new(\n            Options::clone(&base_options)\n                .with_merged_layout(layout_config.as_ref())\n                .adjusted_for_scale(scale.fractional_scale()),\n        );\n\n        let view_size = output_size(&output);\n        let working_area = compute_working_area(&output);\n\n        let scrolling = ScrollingSpace::new(\n            view_size,\n            working_area,\n            scale.fractional_scale(),\n            clock.clone(),\n            options.clone(),\n        );\n\n        let floating = FloatingSpace::new(\n            view_size,\n            working_area,\n            scale.fractional_scale(),\n            clock.clone(),\n            options.clone(),\n        );\n\n        let shadow_config =\n            compute_workspace_shadow_config(options.overview.workspace_shadow, view_size);\n\n        Self {\n            scrolling,\n            floating,\n            floating_is_active: FloatingActive::No,\n            original_output,\n            scale,\n            transform: output.current_transform(),\n            view_size,\n            working_area,\n            shadow: Shadow::new(shadow_config),\n            background_buffer: SolidColorBuffer::new(view_size, options.layout.background_color),\n            output: Some(output),\n            clock,\n            base_options,\n            options,\n            name: config.map(|c| c.name.0),\n            layout_config,\n            id: WorkspaceId::next(),\n        }\n    }\n\n    pub fn new_with_config_no_outputs(\n        mut config: Option<WorkspaceConfig>,\n        clock: Clock,\n        base_options: Rc<Options>,\n    ) -> Self {\n        let original_output = OutputId(\n            config\n                .as_ref()\n                .and_then(|c| c.open_on_output.clone())\n                .unwrap_or_default(),\n        );\n\n        let layout_config = config.as_mut().and_then(|c| c.layout.take().map(|x| x.0));\n\n        let scale = smithay::output::Scale::Integer(1);\n        let options = Rc::new(\n            Options::clone(&base_options)\n                .with_merged_layout(layout_config.as_ref())\n                .adjusted_for_scale(scale.fractional_scale()),\n        );\n\n        let view_size = Size::from((1280., 720.));\n        let working_area = Rectangle::from_size(Size::from((1280., 720.)));\n\n        let scrolling = ScrollingSpace::new(\n            view_size,\n            working_area,\n            scale.fractional_scale(),\n            clock.clone(),\n            options.clone(),\n        );\n\n        let floating = FloatingSpace::new(\n            view_size,\n            working_area,\n            scale.fractional_scale(),\n            clock.clone(),\n            options.clone(),\n        );\n\n        let shadow_config =\n            compute_workspace_shadow_config(options.overview.workspace_shadow, view_size);\n\n        Self {\n            scrolling,\n            floating,\n            floating_is_active: FloatingActive::No,\n            output: None,\n            scale,\n            transform: Transform::Normal,\n            original_output,\n            view_size,\n            working_area,\n            shadow: Shadow::new(shadow_config),\n            background_buffer: SolidColorBuffer::new(view_size, options.layout.background_color),\n            clock,\n            base_options,\n            options,\n            name: config.map(|c| c.name.0),\n            layout_config,\n            id: WorkspaceId::next(),\n        }\n    }\n\n    pub fn new_no_outputs(clock: Clock, options: Rc<Options>) -> Self {\n        Self::new_with_config_no_outputs(None, clock, options)\n    }\n\n    pub fn id(&self) -> WorkspaceId {\n        self.id\n    }\n\n    pub fn name(&self) -> Option<&String> {\n        self.name.as_ref()\n    }\n\n    pub fn unname(&mut self) {\n        self.name = None;\n    }\n\n    pub fn has_windows_or_name(&self) -> bool {\n        self.has_windows() || self.name.is_some()\n    }\n\n    pub fn scale(&self) -> smithay::output::Scale {\n        self.scale\n    }\n\n    pub fn advance_animations(&mut self) {\n        self.scrolling.advance_animations();\n        self.floating.advance_animations();\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        self.scrolling.are_animations_ongoing() || self.floating.are_animations_ongoing()\n    }\n\n    pub fn are_transitions_ongoing(&self) -> bool {\n        self.scrolling.are_transitions_ongoing() || self.floating.are_transitions_ongoing()\n    }\n\n    pub fn update_render_elements(&mut self, is_active: bool) {\n        self.scrolling\n            .update_render_elements(is_active && !self.floating_is_active.get());\n\n        let view_rect = Rectangle::from_size(self.view_size);\n        self.floating\n            .update_render_elements(is_active && self.floating_is_active.get(), view_rect);\n\n        self.shadow.update_render_elements(\n            self.view_size,\n            true,\n            CornerRadius::default(),\n            self.scale.fractional_scale(),\n            1.,\n        );\n    }\n\n    pub fn update_config(&mut self, base_options: Rc<Options>) {\n        let scale = self.scale.fractional_scale();\n        let options = Rc::new(\n            Options::clone(&base_options)\n                .with_merged_layout(self.layout_config.as_ref())\n                .adjusted_for_scale(scale),\n        );\n\n        self.scrolling.update_config(\n            self.view_size,\n            self.working_area,\n            self.scale.fractional_scale(),\n            options.clone(),\n        );\n\n        self.floating.update_config(\n            self.view_size,\n            self.working_area,\n            self.scale.fractional_scale(),\n            options.clone(),\n        );\n\n        let shadow_config =\n            compute_workspace_shadow_config(options.overview.workspace_shadow, self.view_size);\n        self.shadow.update_config(shadow_config);\n\n        self.background_buffer\n            .set_color(options.layout.background_color);\n\n        self.base_options = base_options;\n        self.options = options;\n    }\n\n    pub fn update_layout_config(&mut self, layout_config: Option<niri_config::LayoutPart>) {\n        if self.layout_config == layout_config {\n            return;\n        }\n\n        self.layout_config = layout_config;\n        self.update_config(self.base_options.clone());\n    }\n\n    pub fn update_shaders(&mut self) {\n        self.scrolling.update_shaders();\n        self.floating.update_shaders();\n        self.shadow.update_shaders();\n    }\n\n    pub fn windows(&self) -> impl Iterator<Item = &W> + '_ {\n        self.tiles().map(Tile::window)\n    }\n\n    pub fn windows_mut(&mut self) -> impl Iterator<Item = &mut W> + '_ {\n        self.tiles_mut().map(Tile::window_mut)\n    }\n\n    pub fn tiles(&self) -> impl Iterator<Item = &Tile<W>> + '_ {\n        let scrolling = self.scrolling.tiles();\n        let floating = self.floating.tiles();\n        scrolling.chain(floating)\n    }\n\n    pub fn tiles_mut(&mut self) -> impl Iterator<Item = &mut Tile<W>> + '_ {\n        let scrolling = self.scrolling.tiles_mut();\n        let floating = self.floating.tiles_mut();\n        scrolling.chain(floating)\n    }\n\n    pub fn is_floating(&self, id: &W::Id) -> bool {\n        self.floating.has_window(id)\n    }\n\n    pub fn current_output(&self) -> Option<&Output> {\n        self.output.as_ref()\n    }\n\n    pub fn active_window(&self) -> Option<&W> {\n        if self.floating_is_active.get() {\n            self.floating.active_window()\n        } else {\n            self.scrolling.active_window()\n        }\n    }\n\n    pub fn active_window_mut(&mut self) -> Option<&mut W> {\n        if self.floating_is_active.get() {\n            self.floating.active_window_mut()\n        } else {\n            self.scrolling.active_window_mut()\n        }\n    }\n\n    pub fn is_active_pending_fullscreen(&self) -> bool {\n        self.scrolling.is_active_pending_fullscreen()\n    }\n\n    pub fn set_output(&mut self, output: Option<Output>) {\n        if self.output == output {\n            return;\n        }\n\n        if let Some(output) = self.output.take() {\n            for win in self.windows() {\n                win.output_leave(&output);\n            }\n        }\n\n        self.output = output;\n\n        if let Some(output) = &self.output {\n            // Normalize original output: possibly replace connector with make/model/serial.\n            if self.original_output.matches(output) {\n                self.original_output = OutputId::new(output);\n            }\n\n            self.update_output_size();\n\n            for win in self.windows() {\n                self.enter_output_for_window(win);\n            }\n        }\n    }\n\n    fn enter_output_for_window(&self, window: &W) {\n        if let Some(output) = &self.output {\n            window.set_preferred_scale_transform(self.scale, self.transform);\n            window.output_enter(output);\n        }\n    }\n\n    pub fn update_output_size(&mut self) {\n        let output = self.output.as_ref().unwrap();\n        let scale = output.current_scale();\n        let transform = output.current_transform();\n        let view_size = output_size(output);\n        let working_area = compute_working_area(output);\n        self.set_view_size(scale, transform, view_size, working_area);\n    }\n\n    fn set_view_size(\n        &mut self,\n        scale: smithay::output::Scale,\n        transform: Transform,\n        size: Size<f64, Logical>,\n        working_area: Rectangle<f64, Logical>,\n    ) {\n        let scale_transform_changed = self.transform != transform\n            || self.scale.integer_scale() != scale.integer_scale()\n            || self.scale.fractional_scale() != scale.fractional_scale();\n        if !scale_transform_changed && self.view_size == size && self.working_area == working_area {\n            return;\n        }\n\n        let fractional_scale_changed = self.scale.fractional_scale() != scale.fractional_scale();\n\n        self.scale = scale;\n        self.transform = transform;\n        self.view_size = size;\n        self.working_area = working_area;\n\n        if fractional_scale_changed {\n            // Options need to be recomputed for the new scale.\n            self.update_config(self.base_options.clone());\n        } else {\n            // Pass our existing options as is.\n            self.scrolling.update_config(\n                size,\n                working_area,\n                scale.fractional_scale(),\n                self.options.clone(),\n            );\n            self.floating.update_config(\n                size,\n                working_area,\n                scale.fractional_scale(),\n                self.options.clone(),\n            );\n\n            let shadow_config =\n                compute_workspace_shadow_config(self.options.overview.workspace_shadow, size);\n            self.shadow.update_config(shadow_config);\n        }\n\n        self.background_buffer.resize(size);\n\n        if scale_transform_changed {\n            for window in self.windows() {\n                window.set_preferred_scale_transform(self.scale, self.transform);\n            }\n        }\n    }\n\n    pub fn view_size(&self) -> Size<f64, Logical> {\n        self.view_size\n    }\n\n    pub fn make_tile(&self, window: W) -> Tile<W> {\n        Tile::new(\n            window,\n            self.view_size,\n            self.scale.fractional_scale(),\n            self.clock.clone(),\n            self.options.clone(),\n        )\n    }\n\n    pub fn add_tile(\n        &mut self,\n        mut tile: Tile<W>,\n        target: WorkspaceAddWindowTarget<W>,\n        activate: ActivateWindow,\n        width: ColumnWidth,\n        is_full_width: bool,\n        is_floating: bool,\n    ) {\n        self.enter_output_for_window(tile.window());\n        tile.restore_to_floating = is_floating;\n\n        match target {\n            WorkspaceAddWindowTarget::Auto => {\n                // Don't steal focus from an active fullscreen window.\n                let activate = activate.map_smart(|| !self.is_active_pending_fullscreen());\n\n                // If the tile is pending maximized or fullscreen, open it in the scrolling layout\n                // where it can do that.\n                if is_floating && tile.window().pending_sizing_mode().is_normal() {\n                    self.floating.add_tile(tile, activate);\n\n                    if activate || self.scrolling.is_empty() {\n                        self.floating_is_active = FloatingActive::Yes;\n                    }\n                } else {\n                    self.scrolling\n                        .add_tile(None, tile, activate, width, is_full_width, None);\n\n                    if activate {\n                        self.floating_is_active = FloatingActive::No;\n                    }\n                }\n            }\n            WorkspaceAddWindowTarget::NewColumnAt(col_idx) => {\n                let activate = activate.map_smart(|| false);\n                self.scrolling\n                    .add_tile(Some(col_idx), tile, activate, width, is_full_width, None);\n\n                if activate {\n                    self.floating_is_active = FloatingActive::No;\n                }\n            }\n            WorkspaceAddWindowTarget::NextTo(next_to) => {\n                let activate = activate.map_smart(|| self.active_window().unwrap().id() == next_to);\n\n                let floating_has_window = self.floating.has_window(next_to);\n\n                if is_floating && tile.window().pending_sizing_mode().is_normal() {\n                    if floating_has_window {\n                        self.floating.add_tile_above(next_to, tile, activate);\n                    } else {\n                        // FIXME: use static pos\n                        let (next_to_tile, render_pos, _visible) = self\n                            .scrolling\n                            .tiles_with_render_positions()\n                            .find(|(tile, _, _)| tile.window().id() == next_to)\n                            .unwrap();\n\n                        // Position the new tile in the center above the next_to tile. Think a\n                        // dialog opening on top of a window.\n                        let tile_size = tile.tile_size();\n                        let pos = render_pos\n                            + (next_to_tile.tile_size().to_point() - tile_size.to_point())\n                                .downscale(2.);\n                        let pos = self.floating.clamp_within_working_area(pos, tile_size);\n                        let pos = self.floating.logical_to_size_frac(pos);\n                        tile.floating_pos = Some(pos);\n\n                        self.floating.add_tile(tile, activate);\n                    }\n\n                    if activate || self.scrolling.is_empty() {\n                        self.floating_is_active = FloatingActive::Yes;\n                    }\n                } else if floating_has_window {\n                    self.scrolling\n                        .add_tile(None, tile, activate, width, is_full_width, None);\n\n                    if activate {\n                        self.floating_is_active = FloatingActive::No;\n                    }\n                } else {\n                    self.scrolling\n                        .add_tile_right_of(next_to, tile, activate, width, is_full_width);\n\n                    if activate {\n                        self.floating_is_active = FloatingActive::No;\n                    }\n                }\n            }\n        }\n    }\n\n    pub fn add_tile_to_column(\n        &mut self,\n        col_idx: usize,\n        tile_idx: Option<usize>,\n        tile: Tile<W>,\n        activate: bool,\n    ) {\n        self.enter_output_for_window(tile.window());\n        self.scrolling\n            .add_tile_to_column(col_idx, tile_idx, tile, activate);\n\n        if activate {\n            self.floating_is_active = FloatingActive::No;\n        }\n    }\n\n    pub fn add_column(&mut self, column: Column<W>, activate: bool) {\n        for (tile, _) in column.tiles() {\n            self.enter_output_for_window(tile.window());\n        }\n\n        self.scrolling.add_column(None, column, activate, None);\n\n        if activate {\n            self.floating_is_active = FloatingActive::No;\n        }\n    }\n\n    fn update_focus_floating_tiling_after_removing(&mut self, removed_from_floating: bool) {\n        if removed_from_floating {\n            if self.floating.is_empty() {\n                self.floating_is_active = FloatingActive::No;\n            }\n        } else {\n            // Scrolling should remain focused if both are empty.\n            if self.scrolling.is_empty() && !self.floating.is_empty() {\n                self.floating_is_active = FloatingActive::Yes;\n            }\n        }\n    }\n\n    pub fn remove_tile(&mut self, id: &W::Id, transaction: Transaction) -> RemovedTile<W> {\n        let mut from_floating = false;\n        let removed = if self.floating.has_window(id) {\n            from_floating = true;\n            self.floating.remove_tile(id)\n        } else {\n            self.scrolling.remove_tile(id, transaction)\n        };\n\n        if let Some(output) = &self.output {\n            removed.tile.window().output_leave(output);\n        }\n\n        self.update_focus_floating_tiling_after_removing(from_floating);\n\n        removed\n    }\n\n    pub fn remove_active_tile(&mut self, transaction: Transaction) -> Option<RemovedTile<W>> {\n        let from_floating = self.floating_is_active.get();\n        let removed = if from_floating {\n            self.floating.remove_active_tile()?\n        } else {\n            self.scrolling.remove_active_tile(transaction)?\n        };\n\n        if let Some(output) = &self.output {\n            removed.tile.window().output_leave(output);\n        }\n\n        self.update_focus_floating_tiling_after_removing(from_floating);\n\n        Some(removed)\n    }\n\n    pub fn remove_active_column(&mut self) -> Option<Column<W>> {\n        let from_floating = self.floating_is_active.get();\n        if from_floating {\n            return None;\n        }\n\n        let column = self.scrolling.remove_active_column()?;\n\n        if let Some(output) = &self.output {\n            for (tile, _) in column.tiles() {\n                tile.window().output_leave(output);\n            }\n        }\n\n        self.update_focus_floating_tiling_after_removing(from_floating);\n\n        Some(column)\n    }\n\n    pub fn resolve_default_width(\n        &self,\n        default_width: Option<Option<PresetSize>>,\n        is_floating: bool,\n    ) -> Option<PresetSize> {\n        match default_width {\n            Some(Some(width)) => Some(width),\n            Some(None) => None,\n            None if is_floating => None,\n            None => self.options.layout.default_column_width,\n        }\n    }\n\n    pub fn resolve_default_height(\n        &self,\n        default_height: Option<Option<PresetSize>>,\n        is_floating: bool,\n    ) -> Option<PresetSize> {\n        match default_height {\n            Some(Some(height)) => Some(height),\n            Some(None) => None,\n            None if is_floating => None,\n            // We don't have a global default at the moment.\n            None => None,\n        }\n    }\n\n    pub fn new_window_size(\n        &self,\n        width: Option<PresetSize>,\n        height: Option<PresetSize>,\n        is_floating: bool,\n        rules: &ResolvedWindowRules,\n        (min_size, max_size): (Size<i32, Logical>, Size<i32, Logical>),\n    ) -> Size<i32, Logical> {\n        let mut size = if is_floating {\n            self.floating.new_window_size(width, height, rules)\n        } else {\n            self.scrolling.new_window_size(width, height, rules)\n        };\n\n        // If the window has a fixed size, or we're picking some fixed size, apply min and max\n        // size. This is to ensure that a fixed-size window rule works on open, while still\n        // allowing the window freedom to pick its default size otherwise.\n        let (min_size, max_size) = rules.apply_min_max_size(min_size, max_size);\n        size.w = ensure_min_max_size_maybe_zero(size.w, min_size.w, max_size.w);\n        // For scrolling (where height is > 0) only ensure fixed height, since at runtime scrolling\n        // will only honor fixed height currently.\n        if min_size.h == max_size.h {\n            size.h = ensure_min_max_size(size.h, min_size.h, max_size.h);\n        } else if size.h > 0 {\n            // Also always honor min height, scrolling always does.\n            size.h = max(size.h, min_size.h);\n        }\n\n        size\n    }\n\n    pub fn configure_new_window(\n        &self,\n        window: &Window,\n        width: Option<PresetSize>,\n        height: Option<PresetSize>,\n        is_floating: bool,\n        rules: &ResolvedWindowRules,\n    ) {\n        window.with_surfaces(|surface, data| {\n            send_scale_transform(surface, data, self.scale, self.transform);\n        });\n\n        let toplevel = window.toplevel().expect(\"no x11 support\");\n        let (min_size, max_size) = with_states(toplevel.wl_surface(), |state| {\n            let mut guard = state.cached_state.get::<SurfaceCachedState>();\n            let current = guard.current();\n            (current.min_size, current.max_size)\n        });\n        toplevel.with_pending_state(|state| {\n            if state.states.contains(xdg_toplevel::State::Fullscreen) {\n                state.size = Some(self.view_size.to_i32_round());\n            } else if state.states.contains(xdg_toplevel::State::Maximized) {\n                state.size = Some(self.working_area.size.to_i32_round());\n            } else {\n                let size =\n                    self.new_window_size(width, height, is_floating, rules, (min_size, max_size));\n                state.size = Some(size);\n            }\n\n            if is_floating {\n                state.bounds = Some(self.floating.new_window_toplevel_bounds(rules));\n            } else {\n                state.bounds = Some(self.scrolling.new_window_toplevel_bounds(rules));\n            }\n        });\n    }\n\n    pub(super) fn resolve_scrolling_width(\n        &self,\n        window: &W,\n        width: Option<PresetSize>,\n    ) -> ColumnWidth {\n        let width = width.unwrap_or_else(|| PresetSize::Fixed(window.size().w));\n        match width {\n            PresetSize::Fixed(fixed) => {\n                let mut fixed = f64::from(fixed);\n\n                // Add border width since ColumnWidth includes borders.\n                let rules = window.rules();\n                let border = self.options.layout.border.merged_with(&rules.border);\n                if !border.off {\n                    fixed += border.width * 2.;\n                }\n\n                ColumnWidth::Fixed(fixed)\n            }\n            PresetSize::Proportion(prop) => ColumnWidth::Proportion(prop),\n        }\n    }\n\n    pub fn focus_left(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.focus_left()\n        } else {\n            self.scrolling.focus_left()\n        }\n    }\n\n    pub fn focus_right(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.focus_right()\n        } else {\n            self.scrolling.focus_right()\n        }\n    }\n\n    pub fn focus_column_first(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_leftmost();\n        } else {\n            self.scrolling.focus_column_first();\n        }\n    }\n\n    pub fn focus_column_last(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_rightmost();\n        } else {\n            self.scrolling.focus_column_last();\n        }\n    }\n\n    pub fn focus_column_right_or_first(&mut self) {\n        if !self.focus_right() {\n            self.focus_column_first();\n        }\n    }\n\n    pub fn focus_column_left_or_last(&mut self) {\n        if !self.focus_left() {\n            self.focus_column_last();\n        }\n    }\n\n    pub fn focus_column(&mut self, index: usize) {\n        if self.floating_is_active.get() {\n            self.focus_tiling();\n        }\n        self.scrolling.focus_column(index);\n    }\n\n    pub fn focus_window_in_column(&mut self, index: u8) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.focus_window_in_column(index);\n    }\n\n    pub fn focus_down(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.focus_down()\n        } else {\n            self.scrolling.focus_down()\n        }\n    }\n\n    pub fn focus_up(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.focus_up()\n        } else {\n            self.scrolling.focus_up()\n        }\n    }\n\n    pub fn focus_down_or_left(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_down();\n        } else {\n            self.scrolling.focus_down_or_left();\n        }\n    }\n\n    pub fn focus_down_or_right(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_down();\n        } else {\n            self.scrolling.focus_down_or_right();\n        }\n    }\n\n    pub fn focus_up_or_left(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_up();\n        } else {\n            self.scrolling.focus_up_or_left();\n        }\n    }\n\n    pub fn focus_up_or_right(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_up();\n        } else {\n            self.scrolling.focus_up_or_right();\n        }\n    }\n\n    pub fn focus_window_top(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_topmost();\n        } else {\n            self.scrolling.focus_top();\n        }\n    }\n\n    pub fn focus_window_bottom(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.focus_bottommost();\n        } else {\n            self.scrolling.focus_bottom();\n        }\n    }\n\n    pub fn focus_window_down_or_top(&mut self) {\n        if !self.focus_down() {\n            self.focus_window_top();\n        }\n    }\n\n    pub fn focus_window_up_or_bottom(&mut self) {\n        if !self.focus_up() {\n            self.focus_window_bottom();\n        }\n    }\n\n    pub fn move_left(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.move_left();\n            true\n        } else {\n            self.scrolling.move_left()\n        }\n    }\n\n    pub fn move_right(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.move_right();\n            true\n        } else {\n            self.scrolling.move_right()\n        }\n    }\n\n    pub fn move_column_to_first(&mut self) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.move_column_to_first();\n    }\n\n    pub fn move_column_to_last(&mut self) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.move_column_to_last();\n    }\n\n    pub fn move_column_to_index(&mut self, index: usize) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.move_column_to_index(index);\n    }\n\n    pub fn move_down(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.move_down();\n            true\n        } else {\n            self.scrolling.move_down()\n        }\n    }\n\n    pub fn move_up(&mut self) -> bool {\n        if self.floating_is_active.get() {\n            self.floating.move_up();\n            true\n        } else {\n            self.scrolling.move_up()\n        }\n    }\n\n    pub fn consume_or_expel_window_left(&mut self, window: Option<&W::Id>) {\n        if window.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            return;\n        }\n        self.scrolling.consume_or_expel_window_left(window);\n    }\n\n    pub fn consume_or_expel_window_right(&mut self, window: Option<&W::Id>) {\n        if window.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            return;\n        }\n        self.scrolling.consume_or_expel_window_right(window);\n    }\n\n    pub fn consume_into_column(&mut self) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.consume_into_column();\n    }\n\n    pub fn expel_from_column(&mut self) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.expel_from_column();\n    }\n\n    pub fn swap_window_in_direction(&mut self, direction: ScrollDirection) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.swap_window_in_direction(direction);\n    }\n\n    pub fn toggle_column_tabbed_display(&mut self) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.toggle_column_tabbed_display();\n    }\n\n    pub fn set_column_display(&mut self, display: ColumnDisplay) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.set_column_display(display);\n    }\n\n    pub fn center_column(&mut self) {\n        if self.floating_is_active.get() {\n            self.floating.center_window(None);\n        } else {\n            self.scrolling.center_column();\n        }\n    }\n\n    pub fn center_window(&mut self, id: Option<&W::Id>) {\n        if id.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            self.floating.center_window(id);\n        } else {\n            self.scrolling.center_window(id);\n        }\n    }\n\n    pub fn center_visible_columns(&mut self) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.center_visible_columns();\n    }\n\n    pub fn toggle_width(&mut self, forwards: bool) {\n        if self.floating_is_active.get() {\n            self.floating.toggle_window_width(None, forwards);\n        } else {\n            self.scrolling.toggle_width(forwards);\n        }\n    }\n\n    pub fn toggle_full_width(&mut self) {\n        if self.floating_is_active.get() {\n            // Leave this unimplemented for now. For good UX, this probably needs moving the tile\n            // to be against the left edge of the working area while it is full-width.\n            return;\n        }\n        self.scrolling.toggle_full_width();\n    }\n\n    pub fn set_column_width(&mut self, change: SizeChange) {\n        if self.floating_is_active.get() {\n            self.floating.set_window_width(None, change, true);\n        } else {\n            self.scrolling.set_window_width(None, change);\n        }\n    }\n\n    pub fn set_window_width(&mut self, window: Option<&W::Id>, change: SizeChange) {\n        if window.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            self.floating.set_window_width(window, change, true);\n        } else {\n            self.scrolling.set_window_width(window, change);\n        }\n    }\n\n    pub fn set_window_height(&mut self, window: Option<&W::Id>, change: SizeChange) {\n        if window.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            self.floating.set_window_height(window, change, true);\n        } else {\n            self.scrolling.set_window_height(window, change);\n        }\n    }\n\n    pub fn reset_window_height(&mut self, window: Option<&W::Id>) {\n        if window.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            return;\n        }\n        self.scrolling.reset_window_height(window);\n    }\n\n    pub fn toggle_window_width(&mut self, window: Option<&W::Id>, forwards: bool) {\n        if window.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            self.floating.toggle_window_width(window, forwards);\n        } else {\n            self.scrolling.toggle_window_width(window, forwards);\n        }\n    }\n\n    pub fn toggle_window_height(&mut self, window: Option<&W::Id>, forwards: bool) {\n        if window.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            self.floating.toggle_window_height(window, forwards);\n        } else {\n            self.scrolling.toggle_window_height(window, forwards);\n        }\n    }\n\n    pub fn expand_column_to_available_width(&mut self) {\n        if self.floating_is_active.get() {\n            return;\n        }\n        self.scrolling.expand_column_to_available_width();\n    }\n\n    pub fn set_fullscreen(&mut self, window: &W::Id, is_fullscreen: bool) {\n        let mut restore_to_floating = false;\n        if self.floating.has_window(window) {\n            if is_fullscreen {\n                restore_to_floating = true;\n                self.toggle_window_floating(Some(window));\n            } else {\n                // Floating windows are never fullscreen, so this is an unfullscreen request for an\n                // already unfullscreen window.\n                return;\n            }\n        } else if !is_fullscreen {\n            // The window is in the scrolling layout and we're requesting an unfullscreen. If it is\n            // indeed fullscreen (i.e. this isn't a duplicate unfullscreen request), then we may\n            // need to unfullscreen into floating.\n            let col = self\n                .scrolling\n                .columns()\n                .find(|col| col.contains(window))\n                .unwrap();\n\n            // When going from fullscreen to maximized, don't consider restore_to_floating yet.\n            if col.is_pending_fullscreen() && !col.is_pending_maximized() {\n                let (tile, _) = col\n                    .tiles()\n                    .find(|(tile, _)| tile.window().id() == window)\n                    .unwrap();\n                if tile.restore_to_floating {\n                    // Unfullscreen and float in one call so it has a chance to notice and request a\n                    // (0, 0) size, rather than the scrolling column size.\n                    self.toggle_window_floating(Some(window));\n                    return;\n                }\n            }\n        }\n\n        let tile = self\n            .scrolling\n            .tiles()\n            .find(|tile| tile.window().id() == window)\n            .unwrap();\n        let was_normal = tile.window().pending_sizing_mode().is_normal();\n\n        self.scrolling.set_fullscreen(window, is_fullscreen);\n\n        // When going from normal to fullscreen, remember if we should unfullscreen to floating.\n        let tile = self\n            .scrolling\n            .tiles_mut()\n            .find(|tile| tile.window().id() == window)\n            .unwrap();\n        if was_normal && !tile.window().pending_sizing_mode().is_normal() {\n            tile.restore_to_floating = restore_to_floating;\n        }\n    }\n\n    pub fn toggle_fullscreen(&mut self, window: &W::Id) {\n        let tile = self\n            .tiles()\n            .find(|tile| tile.window().id() == window)\n            .unwrap();\n        let current = tile.window().pending_sizing_mode().is_fullscreen();\n        self.set_fullscreen(window, !current);\n    }\n\n    pub fn set_maximized(&mut self, window: &W::Id, maximize: bool) {\n        let mut restore_to_floating = false;\n        if self.floating.has_window(window) {\n            if maximize {\n                restore_to_floating = true;\n                self.toggle_window_floating(Some(window));\n            } else {\n                // Floating windows are never maximized, so this is an unmaximize request for an\n                // already unmaximized window.\n                return;\n            }\n        } else if !maximize {\n            // The window is in the scrolling layout and we're requesting to unmaximize. If it is\n            // indeed maximized (i.e. this isn't a duplicate unmaximize request), then we may\n            // need to unmaximize into floating.\n            let tile = self\n                .scrolling\n                .tiles()\n                .find(|tile| tile.window().id() == window)\n                .unwrap();\n            // The tile cannot unmaximize into fullscreen (pending_sizing_mode() will be fullscreen\n            // in that case and not maximized), so this check works.\n            if tile.window().pending_sizing_mode().is_maximized() && tile.restore_to_floating {\n                // Unmaximize and float in one call so it has a chance to notice and request a\n                // (0, 0) size, rather than the scrolling column size.\n                self.toggle_window_floating(Some(window));\n                return;\n            }\n        }\n\n        let tile = self\n            .scrolling\n            .tiles()\n            .find(|tile| tile.window().id() == window)\n            .unwrap();\n        let was_normal = tile.window().pending_sizing_mode().is_normal();\n\n        self.scrolling.set_maximized(window, maximize);\n\n        // When going from normal to maximized, remember if we should unmaximize to floating.\n        let tile = self\n            .scrolling\n            .tiles_mut()\n            .find(|tile| tile.window().id() == window)\n            .unwrap();\n        if was_normal && !tile.window().pending_sizing_mode().is_normal() {\n            tile.restore_to_floating = restore_to_floating;\n        }\n    }\n\n    pub fn toggle_maximized(&mut self, window: &W::Id) {\n        let mut current = false;\n\n        // We have to check the column property in case the window is in the scrolling layout and\n        // both maximized and fullscreen. In this case, only the column knows whether it's\n        // maximized.\n        //\n        // In the floating layout, windows cannot be maximized.\n        if let Some(col) = self.scrolling.columns().find(|col| col.contains(window)) {\n            current = col.is_pending_maximized();\n        }\n\n        self.set_maximized(window, !current);\n    }\n\n    pub fn toggle_window_floating(&mut self, id: Option<&W::Id>) {\n        let active_id = self.active_window().map(|win| win.id().clone());\n        let target_is_active = id.is_none_or(|id| Some(id) == active_id.as_ref());\n        let Some(id) = id.cloned().or(active_id) else {\n            return;\n        };\n\n        let (_, render_pos, _) = self\n            .tiles_with_render_positions()\n            .find(|(tile, _, _)| *tile.window().id() == id)\n            .unwrap();\n\n        if self.floating.has_window(&id) {\n            let removed = self.floating.remove_tile(&id);\n            // FIXME: compute closest pos?\n            self.scrolling.add_tile(\n                None,\n                removed.tile,\n                target_is_active,\n                removed.width,\n                removed.is_full_width,\n                None,\n            );\n            if target_is_active {\n                self.floating_is_active = FloatingActive::No;\n            }\n        } else {\n            let mut removed = self.scrolling.remove_tile(&id, Transaction::new());\n            removed.tile.stop_move_animations();\n\n            // Come up with a default floating position close to the tile position.\n            let stored_or_default = self.floating.stored_or_default_tile_pos(&removed.tile);\n            if stored_or_default.is_none() {\n                let offset =\n                    if self.options.layout.center_focused_column == CenterFocusedColumn::Always {\n                        Point::from((0., 0.))\n                    } else {\n                        Point::from((50., 50.))\n                    };\n                let pos = render_pos + offset;\n                let size = removed.tile.tile_size();\n                let pos = self.floating.clamp_within_working_area(pos, size);\n                let pos = self.floating.logical_to_size_frac(pos);\n                removed.tile.floating_pos = Some(pos);\n            }\n\n            self.floating.add_tile(removed.tile, target_is_active);\n            if target_is_active {\n                self.floating_is_active = FloatingActive::Yes;\n            }\n        }\n\n        let (tile, new_render_pos) = self\n            .tiles_with_render_positions_mut(false)\n            .find(|(tile, _)| *tile.window().id() == id)\n            .unwrap();\n\n        tile.animate_move_from(render_pos - new_render_pos);\n    }\n\n    pub fn set_window_floating(&mut self, id: Option<&W::Id>, floating: bool) {\n        if id.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) == floating\n        {\n            return;\n        }\n\n        self.toggle_window_floating(id);\n    }\n\n    pub fn focus_floating(&mut self) {\n        if !self.floating_is_active.get() {\n            self.switch_focus_floating_tiling();\n        }\n    }\n\n    pub fn focus_tiling(&mut self) {\n        if self.floating_is_active.get() {\n            self.switch_focus_floating_tiling();\n        }\n    }\n\n    pub fn switch_focus_floating_tiling(&mut self) {\n        if self.floating.is_empty() {\n            // If floating is empty, keep focus on scrolling.\n            return;\n        } else if self.scrolling.is_empty() {\n            // If floating isn't empty but scrolling is, keep focus on floating.\n            return;\n        }\n\n        self.floating_is_active = if self.floating_is_active.get() {\n            FloatingActive::No\n        } else {\n            FloatingActive::Yes\n        };\n    }\n\n    pub fn move_floating_window(\n        &mut self,\n        id: Option<&W::Id>,\n        x: PositionChange,\n        y: PositionChange,\n        animate: bool,\n    ) {\n        if id.map_or(self.floating_is_active.get(), |id| {\n            self.floating.has_window(id)\n        }) {\n            self.floating.move_window(id, x, y, animate);\n        } else {\n            // If the target tile isn't floating, set its stored floating position.\n            let tile = if let Some(id) = id {\n                self.scrolling\n                    .tiles_mut()\n                    .find(|tile| tile.window().id() == id)\n                    .unwrap()\n            } else if let Some(tile) = self.scrolling.active_tile_mut() {\n                tile\n            } else {\n                return;\n            };\n\n            let pos = self.floating.stored_or_default_tile_pos(tile);\n\n            // If there's no stored floating position, we can only set both components at once, not\n            // adjust.\n            let pos = pos.or_else(|| {\n                (matches!(\n                    x,\n                    PositionChange::SetFixed(_) | PositionChange::SetProportion(_)\n                ) && matches!(\n                    y,\n                    PositionChange::SetFixed(_) | PositionChange::SetProportion(_)\n                ))\n                .then_some(Point::default())\n            });\n\n            let Some(mut pos) = pos else {\n                return;\n            };\n\n            let working_area = self.floating.working_area();\n            let available_width = working_area.size.w;\n            let available_height = working_area.size.h;\n            let working_area_loc = working_area.loc;\n\n            const MAX_F: f64 = 10000.;\n\n            match x {\n                PositionChange::SetFixed(x) => pos.x = x + working_area_loc.x,\n                PositionChange::SetProportion(prop) => {\n                    let prop = (prop / 100.).clamp(0., MAX_F);\n                    pos.x = available_width * prop + working_area_loc.x;\n                }\n                PositionChange::AdjustFixed(x) => pos.x += x,\n                PositionChange::AdjustProportion(prop) => {\n                    let current_prop = (pos.x - working_area_loc.x) / available_width.max(1.);\n                    let prop = (current_prop + prop / 100.).clamp(0., MAX_F);\n                    pos.x = available_width * prop + working_area_loc.x;\n                }\n            }\n            match y {\n                PositionChange::SetFixed(y) => pos.y = y + working_area_loc.y,\n                PositionChange::SetProportion(prop) => {\n                    let prop = (prop / 100.).clamp(0., MAX_F);\n                    pos.y = available_height * prop + working_area_loc.y;\n                }\n                PositionChange::AdjustFixed(y) => pos.y += y,\n                PositionChange::AdjustProportion(prop) => {\n                    let current_prop = (pos.y - working_area_loc.y) / available_height.max(1.);\n                    let prop = (current_prop + prop / 100.).clamp(0., MAX_F);\n                    pos.y = available_height * prop + working_area_loc.y;\n                }\n            }\n\n            let pos = self.floating.logical_to_size_frac(pos);\n            tile.floating_pos = Some(pos);\n        }\n    }\n\n    pub fn has_windows(&self) -> bool {\n        self.windows().next().is_some()\n    }\n\n    pub fn has_window(&self, window: &W::Id) -> bool {\n        self.windows().any(|win| win.id() == window)\n    }\n\n    pub fn find_wl_surface(&self, wl_surface: &WlSurface) -> Option<&W> {\n        self.windows().find(|win| win.is_wl_surface(wl_surface))\n    }\n\n    pub fn find_wl_surface_mut(&mut self, wl_surface: &WlSurface) -> Option<&mut W> {\n        self.windows_mut().find(|win| win.is_wl_surface(wl_surface))\n    }\n\n    pub fn tiles_with_render_positions(\n        &self,\n    ) -> impl Iterator<Item = (&Tile<W>, Point<f64, Logical>, bool)> {\n        let scrolling = self.scrolling.tiles_with_render_positions();\n\n        let floating = self.floating.tiles_with_render_positions();\n        let visible = self.is_floating_visible();\n        let floating = floating.map(move |(tile, pos)| (tile, pos, visible));\n\n        floating.chain(scrolling)\n    }\n\n    pub fn tiles_with_render_positions_mut(\n        &mut self,\n        round: bool,\n    ) -> impl Iterator<Item = (&mut Tile<W>, Point<f64, Logical>)> {\n        let scrolling = self.scrolling.tiles_with_render_positions_mut(round);\n        let floating = self.floating.tiles_with_render_positions_mut(round);\n        floating.chain(scrolling)\n    }\n\n    pub fn tiles_with_ipc_layouts(&self) -> impl Iterator<Item = (&Tile<W>, WindowLayout)> {\n        let scrolling = self.scrolling.tiles_with_ipc_layouts();\n        let floating = self.floating.tiles_with_ipc_layouts();\n        floating.chain(scrolling)\n    }\n\n    pub fn active_tile_visual_rectangle(&self) -> Option<Rectangle<f64, Logical>> {\n        if self.floating_is_active.get() {\n            self.floating.active_tile_visual_rectangle()\n        } else {\n            self.scrolling.active_tile_visual_rectangle()\n        }\n    }\n\n    pub fn popup_target_rect(&self, window: &W::Id) -> Option<Rectangle<f64, Logical>> {\n        if self.floating.has_window(window) {\n            self.floating.popup_target_rect(window)\n        } else {\n            self.scrolling.popup_target_rect(window)\n        }\n    }\n\n    pub fn render_scrolling<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        target: RenderTarget,\n        focus_ring: bool,\n        push: &mut dyn FnMut(WorkspaceRenderElement<R>),\n    ) {\n        let scrolling_focus_ring = focus_ring && !self.floating_is_active();\n        self.scrolling\n            .render(renderer, target, scrolling_focus_ring, &mut |elem| {\n                push(elem.into())\n            });\n    }\n\n    pub fn render_floating<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        target: RenderTarget,\n        focus_ring: bool,\n        push: &mut dyn FnMut(WorkspaceRenderElement<R>),\n    ) {\n        if !self.is_floating_visible() {\n            return;\n        }\n\n        let view_rect = Rectangle::from_size(self.view_size);\n        let floating_focus_ring = focus_ring && self.floating_is_active();\n        self.floating.render(\n            renderer,\n            view_rect,\n            target,\n            floating_focus_ring,\n            &mut |elem| push(elem.into()),\n        );\n    }\n\n    pub fn render_shadow<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        push: &mut dyn FnMut(ShadowRenderElement),\n    ) {\n        self.shadow.render(renderer, Point::from((0., 0.)), push);\n    }\n\n    pub fn render_background(&self) -> SolidColorRenderElement {\n        SolidColorRenderElement::from_buffer(\n            &self.background_buffer,\n            Point::new(0., 0.),\n            1.,\n            Kind::Unspecified,\n        )\n    }\n\n    pub fn render_above_top_layer(&self) -> bool {\n        self.scrolling.render_above_top_layer()\n    }\n\n    pub fn is_floating_visible(&self) -> bool {\n        // If the focus is on a fullscreen scrolling window, hide the floating windows.\n        matches!(\n            self.floating_is_active,\n            FloatingActive::Yes | FloatingActive::NoButRaised\n        ) || !self.render_above_top_layer()\n    }\n\n    pub fn store_unmap_snapshot_if_empty(&mut self, renderer: &mut GlesRenderer, window: &W::Id) {\n        let view_size = self.view_size();\n        for (tile, tile_pos) in self.tiles_with_render_positions_mut(false) {\n            if tile.window().id() == window {\n                let view_pos = Point::from((-tile_pos.x, -tile_pos.y));\n                let view_rect = Rectangle::new(view_pos, view_size);\n                tile.update_render_elements(false, view_rect);\n                tile.store_unmap_snapshot_if_empty(renderer);\n                return;\n            }\n        }\n    }\n\n    pub fn clear_unmap_snapshot(&mut self, window: &W::Id) {\n        for tile in self.tiles_mut() {\n            if tile.window().id() == window {\n                let _ = tile.take_unmap_snapshot();\n                return;\n            }\n        }\n    }\n\n    pub fn start_close_animation_for_window(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        window: &W::Id,\n        blocker: TransactionBlocker,\n    ) {\n        if self.floating.has_window(window) {\n            self.floating\n                .start_close_animation_for_window(renderer, window, blocker);\n        } else {\n            self.scrolling\n                .start_close_animation_for_window(renderer, window, blocker);\n        }\n    }\n\n    pub fn start_close_animation_for_tile(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        snapshot: TileRenderSnapshot,\n        tile_size: Size<f64, Logical>,\n        tile_pos: Point<f64, Logical>,\n        blocker: TransactionBlocker,\n    ) {\n        self.floating\n            .start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);\n    }\n\n    pub fn start_open_animation(&mut self, id: &W::Id) -> bool {\n        self.scrolling.start_open_animation(id) || self.floating.start_open_animation(id)\n    }\n\n    pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<(&W, HitType)> {\n        // This logic is consistent with tiles_with_render_positions().\n        if self.is_floating_visible() {\n            if let Some(rv) = self\n                .floating\n                .tiles_with_render_positions()\n                .find_map(|(tile, tile_pos)| HitType::hit_tile(tile, tile_pos, pos))\n            {\n                return Some(rv);\n            }\n        }\n\n        self.scrolling.window_under(pos)\n    }\n\n    pub fn resize_edges_under(&self, pos: Point<f64, Logical>) -> Option<ResizeEdge> {\n        self.tiles_with_render_positions()\n            .find_map(|(tile, tile_pos, visible)| {\n                // This logic should be consistent with window_under() in when it returns Some vs.\n                // None.\n                if !visible {\n                    return None;\n                }\n\n                let pos_within_tile = pos - tile_pos;\n\n                if tile.hit(pos_within_tile).is_some() {\n                    let size = tile.tile_size().to_f64();\n\n                    let mut edges = ResizeEdge::empty();\n                    if pos_within_tile.x < size.w / 3. {\n                        edges |= ResizeEdge::LEFT;\n                    } else if 2. * size.w / 3. < pos_within_tile.x {\n                        edges |= ResizeEdge::RIGHT;\n                    }\n                    if pos_within_tile.y < size.h / 3. {\n                        edges |= ResizeEdge::TOP;\n                    } else if 2. * size.h / 3. < pos_within_tile.y {\n                        edges |= ResizeEdge::BOTTOM;\n                    }\n                    return Some(edges);\n                }\n\n                None\n            })\n    }\n\n    pub fn descendants_added(&mut self, id: &W::Id) -> bool {\n        self.floating.descendants_added(id)\n    }\n\n    pub fn update_window(&mut self, window: &W::Id, serial: Option<Serial>) {\n        if !self.floating.update_window(window, serial) {\n            self.scrolling.update_window(window, serial);\n        }\n    }\n\n    pub fn refresh(&mut self, is_active: bool, is_focused: bool) {\n        self.scrolling\n            .refresh(is_active && !self.floating_is_active.get(), is_focused);\n        self.floating\n            .refresh(is_active && self.floating_is_active.get(), is_focused);\n    }\n\n    pub fn scroll_amount_to_activate(&self, window: &W::Id) -> f64 {\n        if self.floating.has_window(window) {\n            return 0.;\n        }\n\n        self.scrolling.scroll_amount_to_activate(window)\n    }\n\n    pub fn is_urgent(&self) -> bool {\n        self.windows().any(|win| win.is_urgent())\n    }\n\n    pub fn activate_window(&mut self, window: &W::Id) -> bool {\n        if self.floating.activate_window(window) {\n            self.floating_is_active = FloatingActive::Yes;\n            true\n        } else if self.scrolling.activate_window(window) {\n            self.floating_is_active = FloatingActive::No;\n            true\n        } else {\n            false\n        }\n    }\n\n    pub fn activate_window_without_raising(&mut self, window: &W::Id) -> bool {\n        if self.floating.activate_window_without_raising(window) {\n            self.floating_is_active = FloatingActive::Yes;\n            true\n        } else if self.scrolling.activate_window(window) {\n            self.floating_is_active = match self.floating_is_active {\n                FloatingActive::No => FloatingActive::No,\n                FloatingActive::NoButRaised => FloatingActive::NoButRaised,\n                FloatingActive::Yes => FloatingActive::NoButRaised,\n            };\n            true\n        } else {\n            false\n        }\n    }\n\n    pub(super) fn scrolling_insert_position(&self, pos: Point<f64, Logical>) -> InsertPosition {\n        self.scrolling.insert_position(pos)\n    }\n\n    pub(super) fn insert_hint_area(\n        &self,\n        position: InsertPosition,\n    ) -> Option<Rectangle<f64, Logical>> {\n        self.scrolling.insert_hint_area(position)\n    }\n\n    pub fn view_offset_gesture_begin(&mut self, is_touchpad: bool) {\n        self.scrolling.view_offset_gesture_begin(is_touchpad);\n    }\n\n    pub fn view_offset_gesture_update(\n        &mut self,\n        delta_x: f64,\n        timestamp: Duration,\n        is_touchpad: bool,\n    ) -> Option<bool> {\n        self.scrolling\n            .view_offset_gesture_update(delta_x, timestamp, is_touchpad)\n    }\n\n    pub fn view_offset_gesture_end(&mut self, is_touchpad: Option<bool>) -> bool {\n        self.scrolling.view_offset_gesture_end(is_touchpad)\n    }\n\n    pub fn dnd_scroll_gesture_begin(&mut self) {\n        self.scrolling.dnd_scroll_gesture_begin();\n    }\n\n    pub fn dnd_scroll_gesture_scroll(&mut self, pos: Point<f64, Logical>, speed: f64) -> bool {\n        let config = &self.options.gestures.dnd_edge_view_scroll;\n        let trigger_width = config.trigger_width;\n\n        // This working area intentionally does not include extra struts from Options.\n        let x = pos.x - self.working_area.loc.x;\n        let width = self.working_area.size.w;\n\n        let x = x.clamp(0., width);\n        let trigger_width = trigger_width.clamp(0., width / 2.);\n\n        let delta = if x < trigger_width {\n            -(trigger_width - x)\n        } else if width - x < trigger_width {\n            trigger_width - (width - x)\n        } else {\n            0.\n        };\n\n        let delta = if trigger_width < 0.01 {\n            // Sanity check for trigger-width 0 or small window sizes.\n            0.\n        } else {\n            // Normalize to [0, 1].\n            delta / trigger_width\n        };\n        let delta = delta * speed;\n\n        self.scrolling.dnd_scroll_gesture_scroll(delta)\n    }\n\n    pub fn dnd_scroll_gesture_end(&mut self) {\n        self.scrolling.dnd_scroll_gesture_end();\n    }\n\n    pub fn interactive_resize_begin(&mut self, window: W::Id, edges: ResizeEdge) -> bool {\n        if self.floating.has_window(&window) {\n            self.floating.interactive_resize_begin(window, edges)\n        } else {\n            self.scrolling.interactive_resize_begin(window, edges)\n        }\n    }\n\n    pub fn interactive_resize_update(\n        &mut self,\n        window: &W::Id,\n        delta: Point<f64, Logical>,\n    ) -> bool {\n        if self.floating.has_window(window) {\n            self.floating.interactive_resize_update(window, delta)\n        } else {\n            self.scrolling.interactive_resize_update(window, delta)\n        }\n    }\n\n    pub fn interactive_resize_end(&mut self, window: Option<&W::Id>) {\n        if let Some(window) = window {\n            if self.floating.has_window(window) {\n                self.floating.interactive_resize_end(Some(window));\n            } else {\n                self.scrolling.interactive_resize_end(Some(window));\n            }\n        } else {\n            self.floating.interactive_resize_end(None);\n            self.scrolling.interactive_resize_end(None);\n        }\n    }\n\n    pub fn floating_is_active(&self) -> bool {\n        self.floating_is_active.get()\n    }\n\n    pub fn floating_logical_to_size_frac(\n        &self,\n        logical_pos: Point<f64, Logical>,\n    ) -> Point<f64, SizeFrac> {\n        self.floating.logical_to_size_frac(logical_pos)\n    }\n\n    pub fn working_area(&self) -> Rectangle<f64, Logical> {\n        self.working_area\n    }\n\n    pub fn layout_config(&self) -> Option<&niri_config::LayoutPart> {\n        self.layout_config.as_ref()\n    }\n\n    #[cfg(test)]\n    pub fn scrolling(&self) -> &ScrollingSpace<W> {\n        &self.scrolling\n    }\n\n    #[cfg(test)]\n    pub fn floating(&self) -> &FloatingSpace<W> {\n        &self.floating\n    }\n\n    #[cfg(test)]\n    pub fn verify_invariants(&self, move_win_id: Option<&W::Id>) {\n        use approx::assert_abs_diff_eq;\n\n        let scale = self.scale.fractional_scale();\n        assert!(scale > 0.);\n        assert!(scale.is_finite());\n\n        let options = Options::clone(&self.base_options)\n            .with_merged_layout(self.layout_config.as_ref())\n            .adjusted_for_scale(scale);\n        assert_eq!(\n            &*self.options, &options,\n            \"options must be base options adjusted for scale\"\n        );\n\n        assert!(self.view_size.w > 0.);\n        assert!(self.view_size.h > 0.);\n\n        assert_eq!(self.background_buffer.size(), self.view_size);\n        assert_eq!(\n            self.background_buffer.color().components(),\n            options.layout.background_color.to_array_unpremul(),\n        );\n\n        assert_eq!(self.view_size, self.scrolling.view_size());\n        assert_eq!(self.working_area, self.scrolling.parent_area());\n        assert_eq!(&self.clock, self.scrolling.clock());\n        assert!(Rc::ptr_eq(&self.options, self.scrolling.options()));\n        self.scrolling.verify_invariants();\n\n        assert_eq!(self.view_size, self.floating.view_size());\n        assert_eq!(self.working_area, self.floating.working_area());\n        assert_eq!(&self.clock, self.floating.clock());\n        assert!(Rc::ptr_eq(&self.options, self.floating.options()));\n        self.floating.verify_invariants();\n\n        if self.floating.is_empty() {\n            assert!(\n                !self.floating_is_active.get(),\n                \"when floating is empty it must never be active\"\n            );\n        } else if self.scrolling.is_empty() {\n            assert!(\n                self.floating_is_active.get(),\n                \"when scrolling is empty but floating isn't, floating should be active\"\n            );\n        }\n\n        for (tile, tile_pos, visible) in self.tiles_with_render_positions() {\n            if Some(tile.window().id()) != move_win_id {\n                assert_eq!(tile.interactive_move_offset, Point::from((0., 0.)));\n            }\n\n            let rounded_pos = tile_pos.to_physical_precise_round(scale).to_logical(scale);\n\n            // Tile positions must be rounded to physical pixels.\n            assert_abs_diff_eq!(tile_pos.x, rounded_pos.x, epsilon = 1e-5);\n            assert_abs_diff_eq!(tile_pos.y, rounded_pos.y, epsilon = 1e-5);\n\n            if let Some(alpha) = &tile.alpha_animation {\n                let anim = &alpha.anim;\n                if visible {\n                    assert_eq!(anim.to(), 1., \"visible tiles can animate alpha only to 1\");\n                }\n\n                assert!(\n                    !alpha.hold_after_done,\n                    \"tiles in the layout cannot have held alpha animation\"\n                );\n            }\n        }\n    }\n}\n\npub(super) fn compute_working_area(output: &Output) -> Rectangle<f64, Logical> {\n    layer_map_for_output(output).non_exclusive_zone().to_f64()\n}\n\nfn compute_workspace_shadow_config(\n    config: niri_config::WorkspaceShadow,\n    view_size: Size<f64, Logical>,\n) -> niri_config::Shadow {\n    // Gaps between workspaces are a multiple of the view height, so shadow settings should also be\n    // normalized to the view height to prevent them from overlapping on lower resolutions.\n    let norm = view_size.h / 1080.;\n\n    let mut config = niri_config::Shadow::from(config);\n    config.softness *= norm;\n    config.spread *= norm;\n    config.offset.x.0 *= norm;\n    config.offset.y.0 *= norm;\n\n    config\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#[macro_use]\nextern crate tracing;\n\n#[cfg(feature = \"dbus\")]\npub mod a11y;\npub mod animation;\npub mod backend;\npub mod cli;\npub mod cursor;\n#[cfg(feature = \"dbus\")]\npub mod dbus;\npub mod frame_clock;\npub mod handlers;\npub mod input;\npub mod ipc;\npub mod layer;\npub mod layout;\npub mod niri;\npub mod protocols;\npub mod render_helpers;\npub mod rubber_band;\n#[cfg(feature = \"xdp-gnome-screencast\")]\npub mod screencasting;\npub mod ui;\npub mod utils;\npub mod window;\n\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "src/main.rs",
    "content": "#[macro_use]\nextern crate tracing;\n\nuse std::fmt::Write as _;\nuse std::fs::File;\nuse std::io::{self, Write};\nuse std::os::fd::FromRawFd;\nuse std::path::PathBuf;\nuse std::process::Command;\nuse std::sync::atomic::Ordering;\nuse std::{env, mem};\n\nuse calloop::EventLoop;\nuse clap::{CommandFactory, Parser};\nuse clap_complete::Shell;\nuse clap_complete_nushell::Nushell;\nuse directories::ProjectDirs;\nuse niri::cli::{Cli, CompletionShell, Sub};\n#[cfg(feature = \"dbus\")]\nuse niri::dbus;\nuse niri::ipc::client::handle_msg;\nuse niri::niri::State;\nuse niri::utils::spawning::{\n    spawn, spawn_sh, store_and_increase_nofile_rlimit, CHILD_DISPLAY, CHILD_ENV,\n    REMOVE_ENV_RUST_BACKTRACE, REMOVE_ENV_RUST_LIB_BACKTRACE,\n};\nuse niri::utils::{cause_panic, version, watcher, xwayland, IS_SYSTEMD_SERVICE};\nuse niri_config::{Config, ConfigPath};\nuse niri_ipc::socket::SOCKET_PATH_ENV;\nuse sd_notify::NotifyState;\nuse smithay::reexports::wayland_server::Display;\nuse tracing_subscriber::EnvFilter;\n\nconst DEFAULT_LOG_FILTER: &str = \"niri=debug,smithay::backend::renderer::gles=error\";\n\n#[cfg(feature = \"profile-with-tracy-allocations\")]\n#[global_allocator]\nstatic GLOBAL: tracy_client::ProfiledAllocator<std::alloc::System> =\n    tracy_client::ProfiledAllocator::new(std::alloc::System, 100);\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    // Set backtrace defaults if not set.\n    if env::var_os(\"RUST_BACKTRACE\").is_none() {\n        env::set_var(\"RUST_BACKTRACE\", \"1\");\n        REMOVE_ENV_RUST_BACKTRACE.store(true, Ordering::Relaxed);\n    }\n    if env::var_os(\"RUST_LIB_BACKTRACE\").is_none() {\n        env::set_var(\"RUST_LIB_BACKTRACE\", \"0\");\n        REMOVE_ENV_RUST_LIB_BACKTRACE.store(true, Ordering::Relaxed);\n    }\n\n    let directives = env::var(\"RUST_LOG\").unwrap_or_else(|_| DEFAULT_LOG_FILTER.to_owned());\n    let env_filter = EnvFilter::builder().parse_lossy(directives);\n    tracing_subscriber::fmt()\n        .compact()\n        .with_writer(io::stderr)\n        .with_env_filter(env_filter)\n        .init();\n\n    if env::var_os(\"NOTIFY_SOCKET\").is_some() {\n        IS_SYSTEMD_SERVICE.store(true, Ordering::Relaxed);\n\n        #[cfg(not(feature = \"systemd\"))]\n        warn!(\n            \"running as a systemd service, but systemd support is compiled out. \\\n             Are you sure you did not forget to set `--features systemd`?\"\n        );\n    }\n\n    let cli = Cli::parse();\n\n    if cli.session {\n        // If we're starting as a session, assume that the intention is to start on a TTY unless\n        // this is a WSL environment. Remove DISPLAY, WAYLAND_DISPLAY or WAYLAND_SOCKET from our\n        // environment if they are set, since they will cause the winit backend to be selected\n        // instead.\n        if env::var_os(\"WSL_DISTRO_NAME\").is_none() {\n            if env::var_os(\"DISPLAY\").is_some() {\n                warn!(\"running as a session but DISPLAY is set, removing it\");\n                env::remove_var(\"DISPLAY\");\n            }\n            if env::var_os(\"WAYLAND_DISPLAY\").is_some() {\n                warn!(\"running as a session but WAYLAND_DISPLAY is set, removing it\");\n                env::remove_var(\"WAYLAND_DISPLAY\");\n            }\n            if env::var_os(\"WAYLAND_SOCKET\").is_some() {\n                warn!(\"running as a session but WAYLAND_SOCKET is set, removing it\");\n                env::remove_var(\"WAYLAND_SOCKET\");\n            }\n        }\n\n        // Set the current desktop for xdg-desktop-portal.\n        env::set_var(\"XDG_CURRENT_DESKTOP\", \"niri\");\n        // Ensure the session type is set to Wayland for xdg-autostart and Qt apps.\n        env::set_var(\"XDG_SESSION_TYPE\", \"wayland\");\n    }\n\n    // Handle subcommands.\n    if let Some(subcommand) = cli.subcommand {\n        match subcommand {\n            Sub::Validate { config } => {\n                tracy_client::Client::start();\n\n                config_path(config).load().config?;\n                info!(\"config is valid\");\n                return Ok(());\n            }\n            Sub::Msg { msg, json } => {\n                handle_msg(msg, json)?;\n                return Ok(());\n            }\n            Sub::Panic => cause_panic(),\n            Sub::Completions { shell } => {\n                match shell {\n                    CompletionShell::Nushell => {\n                        clap_complete::generate(\n                            Nushell,\n                            &mut Cli::command(),\n                            \"niri\",\n                            &mut io::stdout(),\n                        );\n                    }\n                    other => {\n                        let generator = Shell::try_from(other).unwrap();\n                        clap_complete::generate(\n                            generator,\n                            &mut Cli::command(),\n                            \"niri\",\n                            &mut io::stdout(),\n                        );\n                    }\n                }\n                return Ok(());\n            }\n        }\n    }\n\n    // Needs to be done before starting Tracy, so that it applies to Tracy's threads.\n    niri::utils::signals::block_early().unwrap();\n\n    // Avoid starting Tracy for the `niri msg` code path since starting/stopping Tracy is a bit\n    // slow.\n    tracy_client::Client::start();\n\n    info!(\"starting version {}\", &version());\n\n    // Load the config.\n    let config_path = config_path(cli.config);\n    env::remove_var(\"NIRI_CONFIG\");\n    let (config_created_at, config_load_result) = config_path.load_or_create();\n    let config_errored = config_load_result.config.is_err();\n    let mut config = config_load_result.config.unwrap_or_else(|err| {\n        warn!(\"{err:?}\");\n        Config::load_default()\n    });\n    let config_includes = config_load_result.includes;\n\n    let spawn_at_startup = mem::take(&mut config.spawn_at_startup);\n    let spawn_sh_at_startup = mem::take(&mut config.spawn_sh_at_startup);\n    *CHILD_ENV.write().unwrap() = mem::take(&mut config.environment);\n\n    store_and_increase_nofile_rlimit();\n\n    // Create the main event loop.\n    let mut event_loop = EventLoop::<State>::try_new().unwrap();\n\n    // Handle Ctrl+C and other signals.\n    niri::utils::signals::listen(&event_loop.handle());\n\n    // Create the compositor.\n    let display = Display::new().unwrap();\n\n    // Increase the buffer size so that it's harder to crash a frozen client with a 1000 Hz mouse.\n    display.handle().set_default_max_buffer_size(1024 * 1024);\n\n    let mut state = State::new(\n        config,\n        event_loop.handle(),\n        event_loop.get_signal(),\n        display,\n        false,\n        true,\n        cli.session,\n    )\n    .unwrap();\n\n    // Set WAYLAND_DISPLAY for children.\n    let socket_name = state.niri.socket_name.as_deref().unwrap();\n    env::set_var(\"WAYLAND_DISPLAY\", socket_name);\n    info!(\n        \"listening on Wayland socket: {}\",\n        socket_name.to_string_lossy()\n    );\n\n    // Set NIRI_SOCKET for children.\n    if let Some(ipc) = &state.niri.ipc_server {\n        let socket_path = ipc.socket_path.as_deref().unwrap();\n        env::set_var(SOCKET_PATH_ENV, socket_path);\n        info!(\"IPC listening on: {}\", socket_path.to_string_lossy());\n    }\n\n    // Setup xwayland-satellite integration.\n    xwayland::satellite::setup(&mut state);\n    if let Some(satellite) = &state.niri.satellite {\n        let name = satellite.display_name();\n        *CHILD_DISPLAY.write().unwrap() = Some(name.to_owned());\n        env::set_var(\"DISPLAY\", name);\n        info!(\"listening on X11 socket: {name}\");\n    } else {\n        // Avoid spawning children in the host X11.\n        env::remove_var(\"DISPLAY\");\n    }\n\n    if cli.session {\n        // We're starting as a session. Import our variables.\n        import_environment();\n\n        // Inhibit power key handling so we can suspend on it.\n        #[cfg(feature = \"dbus\")]\n        if !state.niri.config.borrow().input.disable_power_key_handling {\n            if let Err(err) = state.niri.inhibit_power_key() {\n                warn!(\"error inhibiting power key: {err:?}\");\n            }\n        }\n    }\n\n    #[cfg(feature = \"dbus\")]\n    dbus::DBusServers::start(&mut state, cli.session);\n\n    #[cfg(feature = \"dbus\")]\n    if cli.session {\n        state.niri.a11y.start();\n    }\n\n    if env::var_os(\"NIRI_DISABLE_SYSTEM_MANAGER_NOTIFY\").is_none_or(|x| x != \"1\") {\n        // Notify systemd we're ready.\n        if let Err(err) = sd_notify::notify(true, &[NotifyState::Ready]) {\n            warn!(\"error notifying systemd: {err:?}\");\n        };\n\n        // Send ready notification to the NOTIFY_FD file descriptor.\n        if let Err(err) = notify_fd() {\n            warn!(\"error notifying fd: {err:?}\");\n        }\n    }\n\n    watcher::setup(&mut state, &config_path, config_includes);\n\n    // Spawn commands from cli and auto-start.\n    spawn(cli.command, None);\n\n    for elem in spawn_at_startup {\n        spawn(elem.command, None);\n    }\n    for elem in spawn_sh_at_startup {\n        spawn_sh(elem.command, None);\n    }\n\n    // Show the config error notification right away if needed.\n    if config_errored {\n        state.niri.config_error_notification.show();\n        state.ipc_config_loaded(true);\n    } else if let Some(path) = config_created_at {\n        state.niri.config_error_notification.show_created(path);\n    }\n\n    // Run the compositor.\n    event_loop\n        .run(None, &mut state, |state| state.refresh_and_flush_clients())\n        .unwrap();\n\n    Ok(())\n}\n\nfn import_environment() {\n    let variables = [\n        \"WAYLAND_DISPLAY\",\n        \"DISPLAY\",\n        \"XDG_CURRENT_DESKTOP\",\n        \"XDG_SESSION_TYPE\",\n        SOCKET_PATH_ENV,\n    ]\n    .join(\" \");\n\n    let mut init_system_import = String::new();\n    if cfg!(feature = \"systemd\") {\n        write!(\n            init_system_import,\n            \"systemctl --user import-environment {variables};\"\n        )\n        .unwrap();\n    }\n    if cfg!(feature = \"dinit\") {\n        write!(init_system_import, \"dinitctl setenv {variables};\").unwrap();\n    }\n\n    let rv = Command::new(\"/bin/sh\")\n        .args([\n            \"-c\",\n            &format!(\n                \"{init_system_import}\\\n                 hash dbus-update-activation-environment 2>/dev/null && \\\n                 dbus-update-activation-environment {variables}\"\n            ),\n        ])\n        .spawn();\n    // Wait for the import process to complete, otherwise services will start too fast without\n    // environment variables available.\n    match rv {\n        Ok(mut child) => match child.wait() {\n            Ok(status) => {\n                if !status.success() {\n                    warn!(\"import environment shell exited with {status}\");\n                }\n            }\n            Err(err) => {\n                warn!(\"error waiting for import environment shell: {err:?}\");\n            }\n        },\n        Err(err) => {\n            warn!(\"error spawning shell to import environment: {err:?}\");\n        }\n    }\n}\n\nfn env_config_path() -> Option<PathBuf> {\n    env::var_os(\"NIRI_CONFIG\")\n        .filter(|x| !x.is_empty())\n        .map(PathBuf::from)\n}\n\nfn default_config_path() -> Option<PathBuf> {\n    let Some(dirs) = ProjectDirs::from(\"\", \"\", \"niri\") else {\n        warn!(\"error retrieving home directory\");\n        return None;\n    };\n\n    let mut path = dirs.config_dir().to_owned();\n    path.push(\"config.kdl\");\n    Some(path)\n}\n\nfn system_config_path() -> PathBuf {\n    PathBuf::from(\"/etc/niri/config.kdl\")\n}\n\nfn config_path(cli_path: Option<PathBuf>) -> ConfigPath {\n    if let Some(explicit) = cli_path.or_else(env_config_path) {\n        return ConfigPath::Explicit(explicit);\n    }\n\n    let system_path = system_config_path();\n\n    if let Some(user_path) = default_config_path() {\n        ConfigPath::Regular {\n            user_path,\n            system_path,\n        }\n    } else {\n        // Couldn't find the home directory, or whatever.\n        ConfigPath::Explicit(system_path)\n    }\n}\n\nfn notify_fd() -> anyhow::Result<()> {\n    let fd = match env::var(\"NOTIFY_FD\") {\n        Ok(notify_fd) => notify_fd.parse()?,\n        Err(env::VarError::NotPresent) => return Ok(()),\n        Err(err) => return Err(err.into()),\n    };\n    env::remove_var(\"NOTIFY_FD\");\n    let mut notif = unsafe { File::from_raw_fd(fd) };\n    notif.write_all(b\"READY=1\\n\")?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/niri.rs",
    "content": "use std::cell::{Cell, OnceCell, RefCell};\nuse std::collections::{HashMap, HashSet};\nuse std::ffi::OsString;\nuse std::os::unix::net::UnixStream;\nuse std::path::PathBuf;\nuse std::rc::Rc;\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::mpsc::{self, Receiver, Sender};\nuse std::sync::{Arc, Mutex};\nuse std::time::{Duration, Instant};\nuse std::{env, mem, thread};\n\nuse _server_decoration::server::org_kde_kwin_server_decoration_manager::Mode as KdeDecorationsMode;\nuse anyhow::{bail, ensure, Context};\nuse calloop::futures::Scheduler;\nuse niri_config::debug::PreviewRender;\nuse niri_config::{\n    Config, FloatOrInt, Key, Modifiers, OutputName, TrackLayout, WarpMouseToFocusMode,\n    WorkspaceReference, Xkb,\n};\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::input::Keycode;\nuse smithay::backend::renderer::damage::OutputDamageTracker;\nuse smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;\nuse smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;\nuse smithay::backend::renderer::element::utils::{\n    select_dmabuf_feedback, CropRenderElement, Relocate, RelocateRenderElement,\n    RescaleRenderElement,\n};\nuse smithay::backend::renderer::element::{\n    default_primary_scanout_output_compare, Element, Id, Kind, PrimaryScanoutOutput,\n    RenderElementStates,\n};\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::backend::renderer::sync::SyncPoint;\nuse smithay::backend::renderer::Color32F;\nuse smithay::desktop::utils::{\n    bbox_from_surface_tree, output_update, send_dmabuf_feedback_surface_tree,\n    send_frames_surface_tree, surface_presentation_feedback_flags_from_states,\n    surface_primary_scanout_output, take_presentation_feedback_surface_tree,\n    under_from_surface_tree, update_surface_primary_scanout_output, OutputPresentationFeedback,\n};\nuse smithay::desktop::{\n    find_popup_root_surface, layer_map_for_output, LayerMap, LayerSurface, PopupGrab, PopupManager,\n    PopupUngrabStrategy, Space, Window, WindowSurfaceType,\n};\nuse smithay::input::keyboard::{Layout as KeyboardLayout, XkbConfig};\nuse smithay::input::pointer::{\n    CursorIcon, CursorImageStatus, CursorImageSurfaceData, Focus,\n    GrabStartData as PointerGrabStartData, MotionEvent,\n};\nuse smithay::input::{Seat, SeatState};\nuse smithay::output::{self, Output, OutputModeSource, PhysicalProperties, Subpixel, WeakOutput};\nuse smithay::reexports::calloop::generic::Generic;\nuse smithay::reexports::calloop::timer::{TimeoutAction, Timer};\nuse smithay::reexports::calloop::{\n    Interest, LoopHandle, LoopSignal, Mode, PostAction, RegistrationToken,\n};\nuse smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_v1::ExtSessionLockV1;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::WmCapabilities;\nuse smithay::reexports::wayland_protocols_misc::server_decoration as _server_decoration;\nuse smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;\nuse smithay::reexports::wayland_server::backend::{\n    ClientData, ClientId, DisconnectReason, GlobalId,\n};\nuse smithay::reexports::wayland_server::protocol::wl_shm;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::reexports::wayland_server::{Client, Display, DisplayHandle, Resource};\nuse smithay::utils::{\n    ClockSource, IsAlive as _, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size,\n    Transform, SERIAL_COUNTER,\n};\nuse smithay::wayland::compositor::{\n    with_states, with_surface_tree_downward, CompositorClientState, CompositorHandler,\n    CompositorState, HookId, SurfaceData, TraversalAction,\n};\nuse smithay::wayland::cursor_shape::CursorShapeManagerState;\nuse smithay::wayland::dmabuf::DmabufState;\nuse smithay::wayland::fractional_scale::FractionalScaleManagerState;\nuse smithay::wayland::idle_inhibit::IdleInhibitManagerState;\nuse smithay::wayland::idle_notify::IdleNotifierState;\nuse smithay::wayland::input_method::InputMethodManagerState;\nuse smithay::wayland::keyboard_shortcuts_inhibit::{\n    KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor,\n};\nuse smithay::wayland::output::OutputManagerState;\nuse smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraintsState};\nuse smithay::wayland::pointer_gestures::PointerGesturesState;\nuse smithay::wayland::presentation::PresentationState;\nuse smithay::wayland::relative_pointer::RelativePointerManagerState;\nuse smithay::wayland::security_context::SecurityContextState;\nuse smithay::wayland::selection::data_device::{set_data_device_selection, DataDeviceState};\nuse smithay::wayland::selection::ext_data_control::DataControlState as ExtDataControlState;\nuse smithay::wayland::selection::primary_selection::PrimarySelectionState;\nuse smithay::wayland::selection::wlr_data_control::DataControlState as WlrDataControlState;\nuse smithay::wayland::session_lock::{LockSurface, SessionLockManagerState, SessionLocker};\nuse smithay::wayland::shell::kde::decoration::KdeDecorationState;\nuse smithay::wayland::shell::wlr_layer::{self, Layer, WlrLayerShellState};\nuse smithay::wayland::shell::xdg::decoration::XdgDecorationState;\nuse smithay::wayland::shell::xdg::XdgShellState;\nuse smithay::wayland::shm::ShmState;\n#[cfg(test)]\nuse smithay::wayland::single_pixel_buffer::SinglePixelBufferState;\nuse smithay::wayland::socket::ListeningSocketSource;\nuse smithay::wayland::tablet_manager::TabletManagerState;\nuse smithay::wayland::text_input::TextInputManagerState;\nuse smithay::wayland::viewporter::ViewporterState;\nuse smithay::wayland::virtual_keyboard::VirtualKeyboardManagerState;\nuse smithay::wayland::xdg_activation::XdgActivationState;\nuse smithay::wayland::xdg_foreign::XdgForeignState;\n\n#[cfg(feature = \"dbus\")]\nuse crate::a11y::A11y;\nuse crate::animation::Clock;\nuse crate::backend::tty::SurfaceDmabufFeedback;\nuse crate::backend::{Backend, Headless, RenderResult, Tty, Winit};\nuse crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor};\n#[cfg(feature = \"dbus\")]\nuse crate::dbus::freedesktop_locale1::Locale1ToNiri;\n#[cfg(feature = \"dbus\")]\nuse crate::dbus::freedesktop_login1::Login1ToNiri;\n#[cfg(feature = \"dbus\")]\nuse crate::dbus::gnome_shell_introspect::{self, IntrospectToNiri, NiriToIntrospect};\n#[cfg(feature = \"dbus\")]\nuse crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri};\nuse crate::frame_clock::FrameClock;\nuse crate::handlers::{configure_lock_surface, XDG_ACTIVATION_TOKEN_TIMEOUT};\nuse crate::input::pick_color_grab::PickColorGrab;\nuse crate::input::scroll_swipe_gesture::ScrollSwipeGesture;\nuse crate::input::scroll_tracker::ScrollTracker;\nuse crate::input::{\n    apply_libinput_settings, mods_with_finger_scroll_binds, mods_with_mouse_binds,\n    mods_with_wheel_binds, TabletData,\n};\nuse crate::ipc::server::IpcServer;\nuse crate::layer::mapped::LayerSurfaceRenderElement;\nuse crate::layer::MappedLayer;\nuse crate::layout::tile::TileRenderElement;\nuse crate::layout::workspace::{Workspace, WorkspaceId};\nuse crate::layout::{\n    HitType, Layout, LayoutElement as _, LayoutElementRenderElement, MonitorRenderElement,\n};\nuse crate::niri_render_elements;\nuse crate::protocols::ext_workspace::{self, ExtWorkspaceManagerState};\nuse crate::protocols::foreign_toplevel::{self, ForeignToplevelManagerState};\nuse crate::protocols::gamma_control::GammaControlManagerState;\nuse crate::protocols::mutter_x11_interop::MutterX11InteropManagerState;\nuse crate::protocols::output_management::OutputManagementManagerState;\nuse crate::protocols::screencopy::{Screencopy, ScreencopyBuffer, ScreencopyManagerState};\nuse crate::protocols::virtual_pointer::VirtualPointerManagerState;\nuse crate::render_helpers::debug::push_opaque_regions;\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::surface::push_elements_from_surface_tree;\nuse crate::render_helpers::texture::TextureBuffer;\nuse crate::render_helpers::{\n    encompassing_geo, render_to_dmabuf, render_to_encompassing_texture, render_to_shm,\n    render_to_texture, render_to_vec, shaders, RenderTarget,\n};\n#[cfg(feature = \"xdp-gnome-screencast\")]\nuse crate::screencasting::Screencasting;\nuse crate::ui::config_error_notification::ConfigErrorNotification;\nuse crate::ui::exit_confirm_dialog::{ExitConfirmDialog, ExitConfirmDialogRenderElement};\nuse crate::ui::hotkey_overlay::HotkeyOverlay;\nuse crate::ui::mru::{MruCloseRequest, WindowMruUi, WindowMruUiRenderElement};\nuse crate::ui::screen_transition::{self, ScreenTransition};\nuse crate::ui::screenshot_ui::{OutputScreenshot, ScreenshotUi, ScreenshotUiRenderElement};\nuse crate::utils::scale::{closest_representable_scale, guess_monitor_scale};\nuse crate::utils::spawning::{CHILD_DISPLAY, CHILD_ENV};\nuse crate::utils::vblank_throttle::VBlankThrottle;\nuse crate::utils::watcher::Watcher;\nuse crate::utils::xwayland::satellite::Satellite;\nuse crate::utils::{\n    center, center_f64, expand_home, get_monotonic_time, ipc_transform_to_smithay, is_mapped,\n    logical_output, make_screenshot_path, output_matches_name, output_size, panel_orientation,\n    send_scale_transform, write_png_rgba8, xwayland,\n};\nuse crate::window::mapped::MappedId;\nuse crate::window::{InitialConfigureState, Mapped, ResolvedWindowRules, Unmapped, WindowRef};\n\nconst CLEAR_COLOR_LOCKED: [f32; 4] = [0.3, 0.1, 0.1, 1.];\n\n// We'll try to send frame callbacks at least once a second. We'll make a timer that fires once a\n// second, so with the worst timing the maximum interval between two frame callbacks for a surface\n// should be ~1.995 seconds.\nconst FRAME_CALLBACK_THROTTLE: Option<Duration> = Some(Duration::from_millis(995));\n\npub struct Niri {\n    pub config: Rc<RefCell<Config>>,\n\n    /// Output config from the config file.\n    ///\n    /// This does not include transient output config changes done via IPC. It is only used when\n    /// reloading the config from disk to determine if the output configuration should be reloaded\n    /// (and transient changes dropped).\n    pub config_file_output_config: niri_config::Outputs,\n\n    pub config_file_watcher: Option<Watcher>,\n\n    pub event_loop: LoopHandle<'static, State>,\n    pub scheduler: Scheduler<()>,\n    pub stop_signal: LoopSignal,\n    pub display_handle: DisplayHandle,\n\n    /// Whether niri was run with `--session`\n    pub is_session_instance: bool,\n\n    /// Name of the Wayland socket.\n    ///\n    /// This is `None` when creating `Niri` without a Wayland socket.\n    pub socket_name: Option<OsString>,\n\n    pub start_time: Instant,\n\n    /// Whether the at-startup=true window rules are active.\n    pub is_at_startup: bool,\n\n    /// Clock for driving animations.\n    pub clock: Clock,\n\n    // Each workspace corresponds to a Space. Each workspace generally has one Output mapped to it,\n    // however it may have none (when there are no outputs connected) or multiple (when mirroring).\n    pub layout: Layout<Mapped>,\n\n    // This space does not actually contain any windows, but all outputs are mapped into it\n    // according to their global position.\n    pub global_space: Space<Window>,\n\n    /// Mapped outputs, sorted by their name and position.\n    pub sorted_outputs: Vec<Output>,\n\n    // Windows which don't have a buffer attached yet.\n    pub unmapped_windows: HashMap<WlSurface, Unmapped>,\n\n    /// Layer surfaces which don't have a buffer attached yet.\n    pub unmapped_layer_surfaces: HashSet<WlSurface>,\n\n    /// Extra data for mapped layer surfaces.\n    pub mapped_layer_surfaces: HashMap<LayerSurface, MappedLayer>,\n\n    // Cached root surface for every surface, so that we can access it in destroyed() where the\n    // normal get_parent() is cleared out.\n    pub root_surface: HashMap<WlSurface, WlSurface>,\n\n    // Dmabuf readiness pre-commit hook for a surface.\n    pub dmabuf_pre_commit_hook: HashMap<WlSurface, HookId>,\n\n    /// Clients to notify about their blockers being cleared.\n    pub blocker_cleared_tx: Sender<Client>,\n    pub blocker_cleared_rx: Receiver<Client>,\n\n    pub output_state: HashMap<Output, OutputState>,\n\n    // When false, we're idling with monitors powered off.\n    pub monitors_active: bool,\n\n    /// Whether the laptop lid is closed.\n    ///\n    /// Libinput guarantees that the lid switch starts in open state, and if it was closed during\n    /// startup, libinput will immediately send a closed event.\n    pub is_lid_closed: bool,\n\n    pub devices: HashSet<input::Device>,\n    pub tablets: HashMap<input::Device, TabletData>,\n    pub touch: HashSet<input::Device>,\n\n    // Smithay state.\n    pub compositor_state: CompositorState,\n    pub xdg_shell_state: XdgShellState,\n    pub xdg_decoration_state: XdgDecorationState,\n    pub kde_decoration_state: KdeDecorationState,\n    pub layer_shell_state: WlrLayerShellState,\n    pub session_lock_state: SessionLockManagerState,\n    pub foreign_toplevel_state: ForeignToplevelManagerState,\n    pub ext_workspace_state: ExtWorkspaceManagerState,\n    pub screencopy_state: ScreencopyManagerState,\n    pub output_management_state: OutputManagementManagerState,\n    pub viewporter_state: ViewporterState,\n    pub xdg_foreign_state: XdgForeignState,\n    pub shm_state: ShmState,\n    pub output_manager_state: OutputManagerState,\n    pub dmabuf_state: DmabufState,\n    pub fractional_scale_manager_state: FractionalScaleManagerState,\n    pub seat_state: SeatState<State>,\n    pub tablet_state: TabletManagerState,\n    pub text_input_state: TextInputManagerState,\n    pub input_method_state: InputMethodManagerState,\n    pub keyboard_shortcuts_inhibit_state: KeyboardShortcutsInhibitState,\n    pub virtual_keyboard_state: VirtualKeyboardManagerState,\n    pub virtual_pointer_state: VirtualPointerManagerState,\n    pub pointer_gestures_state: PointerGesturesState,\n    pub relative_pointer_state: RelativePointerManagerState,\n    pub pointer_constraints_state: PointerConstraintsState,\n    pub idle_notifier_state: IdleNotifierState<State>,\n    pub idle_inhibit_manager_state: IdleInhibitManagerState,\n    pub data_device_state: DataDeviceState,\n    pub primary_selection_state: PrimarySelectionState,\n    pub wlr_data_control_state: WlrDataControlState,\n    pub ext_data_control_state: ExtDataControlState,\n    pub popups: PopupManager,\n    pub popup_grab: Option<PopupGrabState>,\n    pub presentation_state: PresentationState,\n    pub security_context_state: SecurityContextState,\n    pub gamma_control_manager_state: GammaControlManagerState,\n    pub activation_state: XdgActivationState,\n    pub mutter_x11_interop_state: MutterX11InteropManagerState,\n\n    // This will not work as is outside of tests, so it is gated with #[cfg(test)] for now. In\n    // particular, shaders will need to learn about the single pixel buffer. Also, it must be\n    // verified that a black single-pixel-buffer background lets the foreground surface to be\n    // unredirected.\n    //\n    // https://github.com/niri-wm/niri/issues/619\n    #[cfg(test)]\n    pub single_pixel_buffer_state: SinglePixelBufferState,\n\n    pub seat: Seat<State>,\n    /// Scancodes of the keys to suppress.\n    pub suppressed_keys: HashSet<Keycode>,\n    /// Button codes of the mouse buttons to suppress.\n    pub suppressed_buttons: HashSet<u32>,\n    pub bind_cooldown_timers: HashMap<Key, RegistrationToken>,\n    pub bind_repeat_timer: Option<RegistrationToken>,\n    pub keyboard_focus: KeyboardFocus,\n    pub layer_shell_on_demand_focus: Option<LayerSurface>,\n    pub idle_inhibiting_surfaces: HashSet<WlSurface>,\n    pub is_fdo_idle_inhibited: Arc<AtomicBool>,\n    pub keyboard_shortcuts_inhibiting_surfaces: HashMap<WlSurface, KeyboardShortcutsInhibitor>,\n\n    /// Most recent XKB settings from org.freedesktop.locale1.\n    pub xkb_from_locale1: Option<Xkb>,\n\n    pub cursor_manager: CursorManager,\n    pub cursor_texture_cache: CursorTextureCache,\n    pub cursor_shape_manager_state: CursorShapeManagerState,\n    pub dnd_icon: Option<DndIcon>,\n    /// Contents under pointer.\n    ///\n    /// Periodically updated: on motion and other events and in the loop callback. If you require\n    /// the real up-to-date contents somewhere, it's better to recompute on the spot.\n    ///\n    /// This is not pointer focus. I.e. during a click grab, the pointer focus remains on the\n    /// client with the grab, but this field will keep updating to the latest contents as if no\n    /// grab was active.\n    ///\n    /// This is primarily useful for emitting pointer motion events for surfaces that move\n    /// underneath the cursor on their own (i.e. when the tiling layout moves). In this case, not\n    /// taking grabs into account is expected, because we pass the information to pointer.motion()\n    /// which passes it down through grabs, which decide what to do with it as they see fit.\n    pub pointer_contents: PointContents,\n    pub pointer_visibility: PointerVisibility,\n    pub pointer_inactivity_timer: Option<RegistrationToken>,\n    /// Whether the pointer inactivity timer got reset this event loop iteration.\n    ///\n    /// Used for limiting the reset to once per iteration, so that it's not spammed with high\n    /// resolution mice.\n    pub pointer_inactivity_timer_got_reset: bool,\n    /// Whether the (idle notifier) activity was notified this event loop iteration.\n    ///\n    /// Used for limiting the notify to once per iteration, so that it's not spammed with high\n    /// resolution mice.\n    pub notified_activity_this_iteration: bool,\n    pub pointer_inside_hot_corner: bool,\n    pub tablet_cursor_location: Option<Point<f64, Logical>>,\n    pub gesture_swipe_3f_cumulative: Option<(f64, f64)>,\n    pub overview_scroll_swipe_gesture: ScrollSwipeGesture,\n    pub vertical_wheel_tracker: ScrollTracker,\n    pub horizontal_wheel_tracker: ScrollTracker,\n    pub mods_with_mouse_binds: HashSet<Modifiers>,\n    pub mods_with_wheel_binds: HashSet<Modifiers>,\n    pub vertical_finger_scroll_tracker: ScrollTracker,\n    pub horizontal_finger_scroll_tracker: ScrollTracker,\n    pub mods_with_finger_scroll_binds: HashSet<Modifiers>,\n\n    pub lock_state: LockState,\n\n    // State that we last sent to the logind LockedHint.\n    pub locked_hint: Option<bool>,\n\n    pub screenshot_ui: ScreenshotUi,\n    pub config_error_notification: ConfigErrorNotification,\n    pub hotkey_overlay: HotkeyOverlay,\n    pub exit_confirm_dialog: ExitConfirmDialog,\n\n    pub window_mru_ui: WindowMruUi,\n    pub pending_mru_commit: Option<PendingMruCommit>,\n\n    pub pick_window: Option<async_channel::Sender<Option<MappedId>>>,\n    pub pick_color: Option<async_channel::Sender<Option<niri_ipc::PickedColor>>>,\n\n    pub debug_draw_opaque_regions: bool,\n    pub debug_draw_damage: bool,\n\n    #[cfg(feature = \"dbus\")]\n    pub dbus: Option<crate::dbus::DBusServers>,\n    #[cfg(feature = \"dbus\")]\n    pub a11y_keyboard_monitor: Option<crate::dbus::freedesktop_a11y::KeyboardMonitor>,\n    #[cfg(feature = \"dbus\")]\n    pub a11y: A11y,\n    #[cfg(feature = \"dbus\")]\n    pub inhibit_power_key_fd: Option<zbus::zvariant::OwnedFd>,\n\n    pub ipc_server: Option<IpcServer>,\n    pub ipc_outputs_changed: bool,\n\n    pub satellite: Option<Satellite>,\n\n    #[cfg(feature = \"xdp-gnome-screencast\")]\n    pub casting: Screencasting,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub enum PointerVisibility {\n    /// The pointer is visible.\n    Visible,\n    /// The pointer is invisible, but retains its focus.\n    ///\n    /// This state is set temporarily after auto-hiding the pointer to keep tooltips open and grabs\n    /// ongoing.\n    Hidden,\n    /// The pointer is invisible and cannot focus.\n    ///\n    /// Corresponds to a fully disabled pointer, for example after a touchscreen input, or after\n    /// the pointer contents changed in a Hidden state.\n    Disabled,\n}\n\nimpl PointerVisibility {\n    pub fn is_visible(&self) -> bool {\n        matches!(self, Self::Visible)\n    }\n}\n\n#[derive(Debug)]\npub struct DndIcon {\n    pub surface: WlSurface,\n    pub offset: Point<i32, Logical>,\n}\n\npub struct OutputState {\n    pub global: GlobalId,\n    pub frame_clock: FrameClock,\n    pub redraw_state: RedrawState,\n    pub on_demand_vrr_enabled: bool,\n    // After the last redraw, some ongoing animations still remain.\n    pub unfinished_animations_remain: bool,\n    /// Last sequence received in a vblank event.\n    pub last_drm_sequence: Option<u32>,\n    pub vblank_throttle: VBlankThrottle,\n    /// Sequence for frame callback throttling.\n    ///\n    /// We want to send frame callbacks for each surface at most once per monitor refresh cycle.\n    ///\n    /// Even if a surface commit resulted in empty damage to the monitor, we want to delay the next\n    /// frame callback until roughly when a VBlank would occur, had the monitor been damaged. This\n    /// is necessary to prevent clients busy-looping with frame callbacks that result in empty\n    /// damage.\n    ///\n    /// This counter wrapping-increments by 1 every time we move into the next refresh cycle, as\n    /// far as frame callback throttling is concerned. Specifically, it happens:\n    ///\n    /// 1. Upon a successful DRM frame submission. Notably, we don't wait for the VBlank here,\n    ///    because the client buffers are already \"latched\" at the point of submission. Even if a\n    ///    client submits a new buffer right away, we will wait for a VBlank to draw it, which\n    ///    means that busy looping is avoided.\n    /// 2. If a frame resulted in empty damage, a timer is queued to fire roughly when a VBlank\n    ///    would occur, based on the last presentation time and output refresh interval. Sequence\n    ///    is incremented in that timer, before attempting a redraw or sending frame callbacks.\n    pub frame_callback_sequence: u32,\n    /// Solid color buffer for the backdrop that we use instead of clearing to avoid damage\n    /// tracking issues and make screenshots easier.\n    pub backdrop_buffer: SolidColorBuffer,\n    pub lock_render_state: LockRenderState,\n    pub lock_surface: Option<LockSurface>,\n    pub lock_color_buffer: SolidColorBuffer,\n    screen_transition: Option<ScreenTransition>,\n    /// Damage tracker used for the debug damage visualization.\n    pub debug_damage_tracker: OutputDamageTracker,\n}\n\n#[derive(Debug, Default)]\npub enum RedrawState {\n    /// The compositor is idle.\n    #[default]\n    Idle,\n    /// A redraw is queued.\n    Queued,\n    /// We submitted a frame to the KMS and waiting for it to be presented.\n    WaitingForVBlank { redraw_needed: bool },\n    /// We did not submit anything to KMS and made a timer to fire at the estimated VBlank.\n    WaitingForEstimatedVBlank(RegistrationToken),\n    /// A redraw is queued on top of the above.\n    WaitingForEstimatedVBlankAndQueued(RegistrationToken),\n}\n\npub struct PopupGrabState {\n    pub root: WlSurface,\n    pub grab: PopupGrab<State>,\n    pub has_keyboard_grab: bool,\n}\n\n// The surfaces here are always toplevel surfaces focused as far as niri's logic is concerned, even\n// when popup grabs are active (which means the real keyboard focus is on a popup descending from\n// that toplevel surface).\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum KeyboardFocus {\n    // Layout is focused by default if there's nothing else to focus.\n    Layout { surface: Option<WlSurface> },\n    LayerShell { surface: WlSurface },\n    LockScreen { surface: Option<WlSurface> },\n    ScreenshotUi,\n    ExitConfirmDialog,\n    Overview,\n    Mru,\n}\n\n#[derive(Default, Clone, PartialEq)]\npub struct PointContents {\n    // Output under point.\n    pub output: Option<Output>,\n    // Surface under point and its location in the global coordinate space.\n    //\n    // Can be `None` even when `window` is set, for example when the pointer is over the niri\n    // border around the window.\n    pub surface: Option<(WlSurface, Point<f64, Logical>)>,\n    // If surface belongs to a window, this is that window.\n    pub window: Option<(Window, HitType)>,\n    // If surface belongs to a layer surface, this is that layer surface.\n    pub layer: Option<LayerSurface>,\n    // Pointer is over a hot corner.\n    pub hot_corner: bool,\n}\n\n#[derive(Debug, Default)]\npub enum LockState {\n    #[default]\n    Unlocked,\n    WaitingForSurfaces {\n        confirmation: SessionLocker,\n        deadline_token: RegistrationToken,\n    },\n    Locking(SessionLocker),\n    Locked(ExtSessionLockV1),\n}\n\n#[derive(PartialEq, Eq)]\npub enum LockRenderState {\n    /// The output displays a normal session frame.\n    Unlocked,\n    /// The output displays a locked frame.\n    Locked,\n}\n\n// Not related to the one in Smithay.\n//\n// This state keeps track of when a surface last received a frame callback.\nstruct SurfaceFrameThrottlingState {\n    /// Output and sequence that the frame callback was last sent at.\n    last_sent_at: RefCell<Option<(Output, u32)>>,\n}\n\npub enum CenterCoords {\n    Separately,\n    Both,\n    // Force centering even if the cursor is already in the rectangle.\n    BothAlways,\n}\n\n#[derive(Clone, PartialEq, Eq)]\npub enum CastTarget {\n    // Dynamic cast before selecting anything.\n    Nothing,\n    Output {\n        output: WeakOutput,\n        /// Cached name of the output.\n        name: String,\n    },\n    Window {\n        id: u64,\n    },\n}\n\nimpl CastTarget {\n    pub fn output(output: &Output) -> Self {\n        Self::Output {\n            output: output.downgrade(),\n            name: output.name(),\n        }\n    }\n\n    pub fn matches_output(&self, weak: &WeakOutput) -> bool {\n        matches!(self, CastTarget::Output { output, .. } if output == weak)\n    }\n\n    pub fn matches(&self, ipc: &niri_ipc::CastTarget) -> bool {\n        use CastTarget::*;\n        match (self, ipc) {\n            (Nothing, niri_ipc::CastTarget::Nothing {}) => true,\n            (Output { name, .. }, niri_ipc::CastTarget::Output { name: ipc_name }) => {\n                name == ipc_name\n            }\n            (Window { id }, niri_ipc::CastTarget::Window { id: ipc_id }) => id == ipc_id,\n            _ => false,\n        }\n    }\n\n    pub fn make_ipc(&self) -> niri_ipc::CastTarget {\n        use CastTarget::*;\n        match self {\n            Nothing => niri_ipc::CastTarget::Nothing {},\n            Output { name, .. } => niri_ipc::CastTarget::Output { name: name.clone() },\n            Window { id } => niri_ipc::CastTarget::Window { id: *id },\n        }\n    }\n}\n\n/// Pending update to a window's focus timestamp.\n#[derive(Debug)]\npub struct PendingMruCommit {\n    id: MappedId,\n    token: RegistrationToken,\n    stamp: Duration,\n}\n\nimpl RedrawState {\n    fn queue_redraw(self) -> Self {\n        match self {\n            RedrawState::Idle => RedrawState::Queued,\n            RedrawState::WaitingForEstimatedVBlank(token) => {\n                RedrawState::WaitingForEstimatedVBlankAndQueued(token)\n            }\n\n            // A redraw is already queued.\n            value @ (RedrawState::Queued | RedrawState::WaitingForEstimatedVBlankAndQueued(_)) => {\n                value\n            }\n\n            // We're waiting for VBlank, request a redraw afterwards.\n            RedrawState::WaitingForVBlank { .. } => RedrawState::WaitingForVBlank {\n                redraw_needed: true,\n            },\n        }\n    }\n}\n\nimpl Default for SurfaceFrameThrottlingState {\n    fn default() -> Self {\n        Self {\n            last_sent_at: RefCell::new(None),\n        }\n    }\n}\n\nimpl KeyboardFocus {\n    pub fn surface(&self) -> Option<&WlSurface> {\n        match self {\n            KeyboardFocus::Layout { surface } => surface.as_ref(),\n            KeyboardFocus::LayerShell { surface } => Some(surface),\n            KeyboardFocus::LockScreen { surface } => surface.as_ref(),\n            KeyboardFocus::ScreenshotUi => None,\n            KeyboardFocus::ExitConfirmDialog => None,\n            KeyboardFocus::Overview => None,\n            KeyboardFocus::Mru => None,\n        }\n    }\n\n    pub fn into_surface(self) -> Option<WlSurface> {\n        match self {\n            KeyboardFocus::Layout { surface } => surface,\n            KeyboardFocus::LayerShell { surface } => Some(surface),\n            KeyboardFocus::LockScreen { surface } => surface,\n            KeyboardFocus::ScreenshotUi => None,\n            KeyboardFocus::ExitConfirmDialog => None,\n            KeyboardFocus::Overview => None,\n            KeyboardFocus::Mru => None,\n        }\n    }\n\n    pub fn is_layout(&self) -> bool {\n        matches!(self, KeyboardFocus::Layout { .. })\n    }\n\n    pub fn is_overview(&self) -> bool {\n        matches!(self, KeyboardFocus::Overview)\n    }\n}\n\npub struct State {\n    pub backend: Backend,\n    pub niri: Niri,\n}\n\nimpl State {\n    pub fn new(\n        config: Config,\n        event_loop: LoopHandle<'static, State>,\n        stop_signal: LoopSignal,\n        display: Display<State>,\n        headless: bool,\n        create_wayland_socket: bool,\n        is_session_instance: bool,\n    ) -> Result<Self, Box<dyn std::error::Error>> {\n        let _span = tracy_client::span!(\"State::new\");\n\n        let config = Rc::new(RefCell::new(config));\n\n        let has_display = env::var_os(\"WAYLAND_DISPLAY\").is_some()\n            || env::var_os(\"WAYLAND_SOCKET\").is_some()\n            || env::var_os(\"DISPLAY\").is_some();\n\n        let mut backend = if headless {\n            let headless = Headless::new();\n            Backend::Headless(headless)\n        } else if has_display {\n            let winit = Winit::new(config.clone(), event_loop.clone())?;\n            Backend::Winit(winit)\n        } else {\n            let tty = Tty::new(config.clone(), event_loop.clone())\n                .context(\"error initializing the TTY backend\")?;\n            Backend::Tty(tty)\n        };\n\n        let mut niri = Niri::new(\n            config.clone(),\n            event_loop,\n            stop_signal,\n            display,\n            &backend,\n            create_wayland_socket,\n            is_session_instance,\n        );\n        backend.init(&mut niri);\n\n        let mut state = Self { backend, niri };\n\n        // Load the xkb_file config option if set by the user.\n        state.load_xkb_file();\n        // Initialize some IPC server state.\n        state.ipc_keyboard_layouts_changed();\n        // Focus the default monitor if set by the user.\n        state.focus_default_monitor();\n\n        Ok(state)\n    }\n\n    pub fn refresh_and_flush_clients(&mut self) {\n        let _span = tracy_client::span!(\"State::refresh_and_flush_clients\");\n\n        self.refresh();\n\n        // Advance animations to the current time (not target render time) before rendering outputs\n        // in order to clear completed animations and render elements. Even if we're not rendering,\n        // it's good to advance every now and then so the workspace clean-up and animations don't\n        // build up (the 1 second frame callback timer will call this line).\n        self.niri.advance_animations();\n\n        self.niri.redraw_queued_outputs(&mut self.backend);\n\n        {\n            let _span = tracy_client::span!(\"flush_clients\");\n            self.niri.display_handle.flush_clients().unwrap();\n        }\n\n        #[cfg(feature = \"dbus\")]\n        self.niri.update_locked_hint();\n\n        // Clear the time so it's fetched afresh next iteration.\n        self.niri.clock.clear();\n        self.niri.pointer_inactivity_timer_got_reset = false;\n        self.niri.notified_activity_this_iteration = false;\n    }\n\n    // We monitor both libinput and logind: libinput is always there (including without DBus), but\n    // it misses some switch events (e.g. after unsuspend) on some systems.\n    pub fn set_lid_closed(&mut self, is_closed: bool) {\n        if self.niri.is_lid_closed == is_closed {\n            return;\n        }\n\n        debug!(\"laptop lid {}\", if is_closed { \"closed\" } else { \"opened\" });\n        self.niri.is_lid_closed = is_closed;\n        self.backend.on_output_config_changed(&mut self.niri);\n    }\n\n    fn refresh(&mut self) {\n        let _span = tracy_client::span!(\"State::refresh\");\n\n        // Handle commits for surfaces whose blockers cleared this cycle. This should happen before\n        // layout.refresh() since this is where these surfaces handle commits.\n        self.notify_blocker_cleared();\n\n        // These should be called periodically, before flushing the clients.\n        self.niri.popups.cleanup();\n        self.refresh_popup_grab();\n        self.update_keyboard_focus();\n\n        // Should be called before refresh_layout() because that one will refresh other window\n        // states and then send a pending configure.\n        self.niri.refresh_window_states();\n\n        // Needs to be called after updating the keyboard focus.\n        self.niri.refresh_layout();\n\n        self.niri.cursor_manager.check_cursor_image_surface_alive();\n        self.niri.refresh_pointer_outputs();\n        self.niri.global_space.refresh();\n        self.niri.refresh_idle_inhibit();\n        self.refresh_pointer_contents();\n        foreign_toplevel::refresh(self);\n        ext_workspace::refresh(self);\n\n        #[cfg(feature = \"xdp-gnome-screencast\")]\n        self.niri.refresh_mapped_cast_outputs();\n        // Should happen before refresh_window_rules(), but after anything that can start or stop\n        // screencasts.\n        #[cfg(feature = \"xdp-gnome-screencast\")]\n        self.niri.refresh_mapped_cast_window_rules();\n        self.ipc_refresh_casts();\n\n        self.niri.refresh_window_rules();\n        self.refresh_ipc_outputs();\n        self.ipc_refresh_layout();\n        self.ipc_refresh_keyboard_layout_index();\n\n        // Needs to be called after updating the keyboard focus.\n        #[cfg(feature = \"dbus\")]\n        self.niri.refresh_a11y();\n    }\n\n    fn notify_blocker_cleared(&mut self) {\n        let dh = self.niri.display_handle.clone();\n        while let Ok(client) = self.niri.blocker_cleared_rx.try_recv() {\n            trace!(\"calling blocker_cleared\");\n            self.client_compositor_state(&client)\n                .blocker_cleared(self, &dh);\n        }\n    }\n\n    pub fn move_cursor(&mut self, location: Point<f64, Logical>) {\n        let mut under = match self.niri.pointer_visibility {\n            PointerVisibility::Disabled => PointContents::default(),\n            _ => self.niri.contents_under(location),\n        };\n\n        // Disable the hidden pointer if the contents underneath have changed.\n        if !self.niri.pointer_visibility.is_visible() && self.niri.pointer_contents != under {\n            self.niri.pointer_visibility = PointerVisibility::Disabled;\n\n            // When setting PointerVisibility::Hidden together with pointer contents changing,\n            // we can change straight to nothing to avoid one frame of hover. Notably, this can\n            // be triggered through warp-mouse-to-focus combined with hide-when-typing.\n            under = PointContents::default();\n        }\n\n        self.niri.pointer_contents.clone_from(&under);\n\n        let pointer = &self.niri.seat.get_pointer().unwrap();\n        pointer.motion(\n            self,\n            under.surface,\n            &MotionEvent {\n                location,\n                serial: SERIAL_COUNTER.next_serial(),\n                time: get_monotonic_time().as_millis() as u32,\n            },\n        );\n        pointer.frame(self);\n\n        self.niri.maybe_activate_pointer_constraint();\n\n        // We do not show the pointer on programmatic or keyboard movement.\n\n        // FIXME: granular\n        self.niri.queue_redraw_all();\n    }\n\n    /// Moves cursor within the specified rectangle, only adjusting coordinates if needed.\n    fn move_cursor_to_rect(&mut self, rect: Rectangle<f64, Logical>, mode: CenterCoords) -> bool {\n        let pointer = &self.niri.seat.get_pointer().unwrap();\n        let cur_loc = pointer.current_location();\n        let x_in_bound = cur_loc.x >= rect.loc.x && cur_loc.x <= rect.loc.x + rect.size.w;\n        let y_in_bound = cur_loc.y >= rect.loc.y && cur_loc.y <= rect.loc.y + rect.size.h;\n\n        let p = match mode {\n            CenterCoords::Separately => {\n                if x_in_bound && y_in_bound {\n                    return false;\n                } else if y_in_bound {\n                    // adjust x\n                    Point::from((rect.loc.x + rect.size.w / 2.0, cur_loc.y))\n                } else if x_in_bound {\n                    // adjust y\n                    Point::from((cur_loc.x, rect.loc.y + rect.size.h / 2.0))\n                } else {\n                    // adjust x and y\n                    center_f64(rect)\n                }\n            }\n            CenterCoords::Both => {\n                if x_in_bound && y_in_bound {\n                    return false;\n                } else {\n                    // adjust x and y\n                    center_f64(rect)\n                }\n            }\n            CenterCoords::BothAlways => center_f64(rect),\n        };\n\n        self.move_cursor(p);\n        true\n    }\n\n    pub fn move_cursor_to_focused_tile(&mut self, mode: CenterCoords) -> bool {\n        if !self.niri.keyboard_focus.is_layout() {\n            return false;\n        }\n\n        if self.niri.tablet_cursor_location.is_some() {\n            return false;\n        }\n\n        let Some(output) = self.niri.layout.active_output() else {\n            return false;\n        };\n        let monitor = self.niri.layout.monitor_for_output(output).unwrap();\n\n        let mut rv = false;\n        let rect = monitor.active_tile_visual_rectangle();\n\n        if let Some(rect) = rect {\n            let output_geo = self.niri.global_space.output_geometry(output).unwrap();\n            let mut rect = rect;\n            rect.loc += output_geo.loc.to_f64();\n            rv = self.move_cursor_to_rect(rect, mode);\n        }\n\n        rv\n    }\n\n    pub fn focus_default_monitor(&mut self) {\n        // Our default target is the first output in sorted order.\n        let Some(mut target) = self.niri.sorted_outputs.first().cloned() else {\n            // No outputs are connected.\n            return;\n        };\n\n        let config = self.niri.config.borrow();\n        for config in &config.outputs.0 {\n            if !config.focus_at_startup {\n                continue;\n            }\n            if let Some(output) = self.niri.output_by_name_match(&config.name) {\n                target = output.clone();\n                break;\n            }\n        }\n        drop(config);\n\n        self.niri.layout.focus_output(&target);\n        self.move_cursor_to_output(&target);\n    }\n\n    /// Focus a specific window, taking care of a potential active output change and cursor\n    /// warp.\n    pub fn focus_window(&mut self, window: &Window) {\n        let active_output = self.niri.layout.active_output().cloned();\n\n        self.niri.layout.activate_window(window);\n\n        let new_active = self.niri.layout.active_output().cloned();\n        if new_active != active_output {\n            if !self.maybe_warp_cursor_to_focus_centered() {\n                self.move_cursor_to_output(&new_active.unwrap());\n            }\n        } else {\n            self.maybe_warp_cursor_to_focus();\n        }\n\n        // FIXME: granular\n        self.niri.queue_redraw_all();\n    }\n\n    pub fn confirm_mru(&mut self) {\n        if let Some(window) = self.niri.close_mru(MruCloseRequest::Confirm) {\n            self.focus_window(&window);\n        }\n    }\n\n    pub fn maybe_warp_cursor_to_focus(&mut self) -> bool {\n        let focused = match self.niri.config.borrow().input.warp_mouse_to_focus {\n            None => return false,\n            Some(inner) => match inner.mode {\n                None => CenterCoords::Separately,\n                Some(WarpMouseToFocusMode::CenterXy) => CenterCoords::Both,\n                Some(WarpMouseToFocusMode::CenterXyAlways) => CenterCoords::BothAlways,\n            },\n        };\n        self.move_cursor_to_focused_tile(focused)\n    }\n\n    pub fn maybe_warp_cursor_to_focus_centered(&mut self) -> bool {\n        let focused = match self.niri.config.borrow().input.warp_mouse_to_focus {\n            None => return false,\n            Some(inner) => match inner.mode {\n                None => CenterCoords::Both,\n                Some(WarpMouseToFocusMode::CenterXy) => CenterCoords::Both,\n                Some(WarpMouseToFocusMode::CenterXyAlways) => CenterCoords::BothAlways,\n            },\n        };\n        self.move_cursor_to_focused_tile(focused)\n    }\n\n    pub fn refresh_pointer_contents(&mut self) {\n        let _span = tracy_client::span!(\"Niri::refresh_pointer_contents\");\n\n        let pointer = &self.niri.seat.get_pointer().unwrap();\n        let location = pointer.current_location();\n\n        if !self.niri.exit_confirm_dialog.is_open()\n            && !self.niri.is_locked()\n            && !self.niri.screenshot_ui.is_open()\n        {\n            // Don't refresh cursor focus during transitions.\n            if let Some((output, _)) = self.niri.output_under(location) {\n                let monitor = self.niri.layout.monitor_for_output(output).unwrap();\n                if monitor.are_transitions_ongoing() {\n                    return;\n                }\n            }\n        }\n\n        if !self.update_pointer_contents() {\n            return;\n        }\n\n        pointer.frame(self);\n\n        // Pointer motion from a surface to nothing triggers a cursor change to default, which\n        // means we may need to redraw.\n\n        // FIXME: granular\n        self.niri.queue_redraw_all();\n    }\n\n    pub fn update_pointer_contents(&mut self) -> bool {\n        let _span = tracy_client::span!(\"Niri::update_pointer_contents\");\n\n        let pointer = &self.niri.seat.get_pointer().unwrap();\n        let location = pointer.current_location();\n        let mut under = match self.niri.pointer_visibility {\n            PointerVisibility::Disabled => PointContents::default(),\n            _ => self.niri.contents_under(location),\n        };\n\n        // We're not changing the global cursor location here, so if the contents did not change,\n        // then nothing changed.\n        if self.niri.pointer_contents == under {\n            return false;\n        }\n\n        // Disable the hidden pointer if the contents underneath have changed.\n        if !self.niri.pointer_visibility.is_visible() {\n            self.niri.pointer_visibility = PointerVisibility::Disabled;\n\n            // When setting PointerVisibility::Hidden together with pointer contents changing,\n            // we can change straight to nothing to avoid one frame of hover. Notably, this can\n            // be triggered through warp-mouse-to-focus combined with hide-when-typing.\n            under = PointContents::default();\n            if self.niri.pointer_contents == under {\n                return false;\n            }\n        }\n\n        self.niri.pointer_contents.clone_from(&under);\n\n        pointer.motion(\n            self,\n            under.surface,\n            &MotionEvent {\n                location,\n                serial: SERIAL_COUNTER.next_serial(),\n                time: get_monotonic_time().as_millis() as u32,\n            },\n        );\n\n        self.niri.maybe_activate_pointer_constraint();\n\n        true\n    }\n\n    pub fn move_cursor_to_output(&mut self, output: &Output) {\n        let geo = self.niri.global_space.output_geometry(output).unwrap();\n        self.move_cursor(center(geo).to_f64());\n    }\n\n    pub fn refresh_popup_grab(&mut self) {\n        if let Some(grab) = &mut self.niri.popup_grab {\n            if grab.grab.has_ended() {\n                self.niri.popup_grab = None;\n            }\n        }\n    }\n\n    pub fn update_keyboard_focus(&mut self) {\n        // Clean up on-demand layer surface focus if necessary.\n        if let Some(surface) = &self.niri.layer_shell_on_demand_focus {\n            // Still alive and has on-demand interactivity.\n            let mut good = surface.alive()\n                && surface.cached_state().keyboard_interactivity\n                    == wlr_layer::KeyboardInteractivity::OnDemand;\n\n            if let Some(mapped) = self.niri.mapped_layer_surfaces.get(surface) {\n                // Check if it moved to the overview backdrop.\n                if mapped.place_within_backdrop() {\n                    good = false;\n                }\n            } else {\n                // The layer surface is alive but it got unmapped.\n                good = false;\n            }\n\n            if !good {\n                self.niri.layer_shell_on_demand_focus = None;\n            }\n        }\n\n        // Compute the current focus.\n        let focus = if self.niri.exit_confirm_dialog.is_open() {\n            KeyboardFocus::ExitConfirmDialog\n        } else if self.niri.is_locked() {\n            KeyboardFocus::LockScreen {\n                surface: self.niri.lock_surface_focus(),\n            }\n        } else if self.niri.screenshot_ui.is_open() {\n            KeyboardFocus::ScreenshotUi\n        } else if self.niri.window_mru_ui.is_open() {\n            KeyboardFocus::Mru\n        } else if let Some(output) = self.niri.layout.active_output() {\n            let mon = self.niri.layout.monitor_for_output(output).unwrap();\n            let layers = layer_map_for_output(output);\n\n            // Explicitly check for layer-shell popup grabs here, our keyboard focus will stay on\n            // the root layer surface while it has grabs.\n            let layer_grab = self.niri.popup_grab.as_ref().and_then(|g| {\n                layers\n                    .layer_for_surface(&g.root, WindowSurfaceType::TOPLEVEL)\n                    .and_then(|l| l.can_receive_keyboard_focus().then(|| (&g.root, l.layer())))\n            });\n            let grab_on_layer = |layer: Layer| {\n                layer_grab\n                    .and_then(move |(s, l)| if l == layer { Some(s.clone()) } else { None })\n                    .map(|surface| KeyboardFocus::LayerShell { surface })\n            };\n\n            let layout_focus = || {\n                self.niri\n                    .layout\n                    .focus()\n                    .map(|win| win.toplevel().wl_surface().clone())\n                    .map(|surface| KeyboardFocus::Layout {\n                        surface: Some(surface),\n                    })\n            };\n\n            let excl_focus_on_layer = |layer| {\n                layers.layers_on(layer).find_map(|surface| {\n                    if surface.cached_state().keyboard_interactivity\n                        != wlr_layer::KeyboardInteractivity::Exclusive\n                    {\n                        return None;\n                    }\n\n                    let mapped = self.niri.mapped_layer_surfaces.get(surface)?;\n                    if mapped.place_within_backdrop() {\n                        return None;\n                    }\n\n                    let surface = surface.wl_surface().clone();\n                    Some(KeyboardFocus::LayerShell { surface })\n                })\n            };\n\n            let on_d_focus_on_layer = |layer| {\n                layers.layers_on(layer).find_map(|surface| {\n                    let is_on_demand_surface =\n                        Some(surface) == self.niri.layer_shell_on_demand_focus.as_ref();\n                    is_on_demand_surface\n                        .then(|| surface.wl_surface().clone())\n                        .map(|surface| KeyboardFocus::LayerShell { surface })\n                })\n            };\n\n            // Prefer exclusive focus on a layer, then check on-demand focus.\n            let focus_on_layer =\n                |layer| excl_focus_on_layer(layer).or_else(|| on_d_focus_on_layer(layer));\n\n            let is_overview_open = self.niri.layout.is_overview_open();\n\n            let mut surface = grab_on_layer(Layer::Overlay);\n            // FIXME: we shouldn't prioritize the top layer grabs over regular overlay input or a\n            // fullscreen layout window. This will need tracking in grab() to avoid handing it out\n            // in the first place. Or a better way to structure this code.\n            surface = surface.or_else(|| grab_on_layer(Layer::Top));\n\n            if !is_overview_open {\n                surface = surface.or_else(|| grab_on_layer(Layer::Bottom));\n                surface = surface.or_else(|| grab_on_layer(Layer::Background));\n            }\n\n            surface = surface.or_else(|| focus_on_layer(Layer::Overlay));\n\n            if mon.render_above_top_layer() {\n                surface = surface.or_else(layout_focus);\n                surface = surface.or_else(|| focus_on_layer(Layer::Top));\n                surface = surface.or_else(|| focus_on_layer(Layer::Bottom));\n                surface = surface.or_else(|| focus_on_layer(Layer::Background));\n            } else {\n                surface = surface.or_else(|| focus_on_layer(Layer::Top));\n\n                if is_overview_open {\n                    surface = Some(surface.unwrap_or(KeyboardFocus::Overview));\n                }\n\n                surface = surface.or_else(|| on_d_focus_on_layer(Layer::Bottom));\n                surface = surface.or_else(|| on_d_focus_on_layer(Layer::Background));\n                surface = surface.or_else(layout_focus);\n\n                // Bottom and background layers can only receive exclusive focus when there are no\n                // layout windows.\n                surface = surface.or_else(|| excl_focus_on_layer(Layer::Bottom));\n                surface = surface.or_else(|| excl_focus_on_layer(Layer::Background));\n            }\n\n            surface.unwrap_or(KeyboardFocus::Layout { surface: None })\n        } else {\n            KeyboardFocus::Layout { surface: None }\n        };\n\n        let keyboard = self.niri.seat.get_keyboard().unwrap();\n        if self.niri.keyboard_focus != focus {\n            trace!(\n                \"keyboard focus changed from {:?} to {:?}\",\n                self.niri.keyboard_focus,\n                focus\n            );\n\n            // Tell the windows their new focus state for window rule purposes.\n            if let KeyboardFocus::Layout {\n                surface: Some(surface),\n            } = &self.niri.keyboard_focus\n            {\n                if let Some((mapped, _)) = self.niri.layout.find_window_and_output_mut(surface) {\n                    mapped.set_is_focused(false);\n                }\n            }\n            if let KeyboardFocus::Layout {\n                surface: Some(surface),\n            } = &focus\n            {\n                if let Some((mapped, _)) = self.niri.layout.find_window_and_output_mut(surface) {\n                    mapped.set_is_focused(true);\n\n                    // If `mapped` does not have a focus timestamp, then the window is newly\n                    // created/mapped and a timestamp is unconditionally created.\n                    //\n                    // If `mapped` already has a timestamp only update it after the focus lock-in\n                    // period has gone by without the focus having elsewhere.\n                    let stamp = get_monotonic_time();\n\n                    let debounce = self.niri.config.borrow().recent_windows.debounce_ms;\n                    let debounce = Duration::from_millis(u64::from(debounce));\n\n                    if mapped.get_focus_timestamp().is_none() || debounce.is_zero() {\n                        mapped.set_focus_timestamp(stamp);\n                    } else {\n                        let timer = Timer::from_duration(debounce);\n\n                        let focus_token = self\n                            .niri\n                            .event_loop\n                            .insert_source(timer, move |_, _, state| {\n                                state.niri.mru_apply_keyboard_commit();\n                                TimeoutAction::Drop\n                            })\n                            .unwrap();\n                        if let Some(PendingMruCommit { token, .. }) =\n                            self.niri.pending_mru_commit.replace(PendingMruCommit {\n                                id: mapped.id(),\n                                token: focus_token,\n                                stamp,\n                            })\n                        {\n                            self.niri.event_loop.remove(token);\n                        }\n                    }\n                }\n            }\n\n            if let Some(grab) = self.niri.popup_grab.as_mut() {\n                if grab.has_keyboard_grab && Some(&grab.root) != focus.surface() {\n                    trace!(\n                        \"grab root {:?} is not the new focus {:?}, ungrabbing\",\n                        grab.root,\n                        focus\n                    );\n\n                    grab.grab.ungrab(PopupUngrabStrategy::All);\n                    keyboard.unset_grab(self);\n                    self.niri.seat.get_pointer().unwrap().unset_grab(\n                        self,\n                        SERIAL_COUNTER.next_serial(),\n                        get_monotonic_time().as_millis() as u32,\n                    );\n                    self.niri.popup_grab = None;\n                }\n            }\n\n            if self.niri.config.borrow().input.keyboard.track_layout == TrackLayout::Window {\n                let current_layout = keyboard.with_xkb_state(self, |context| {\n                    let xkb = context.xkb().lock().unwrap();\n                    xkb.active_layout()\n                });\n\n                let mut new_layout = current_layout;\n                // Store the currently active layout for the surface.\n                if let Some(current_focus) = self.niri.keyboard_focus.surface() {\n                    with_states(current_focus, |data| {\n                        let cell = data\n                            .data_map\n                            .get_or_insert::<Cell<KeyboardLayout>, _>(Cell::default);\n                        cell.set(current_layout);\n                    });\n                }\n\n                if let Some(focus) = focus.surface() {\n                    new_layout = with_states(focus, |data| {\n                        let cell = data.data_map.get_or_insert::<Cell<KeyboardLayout>, _>(|| {\n                            // The default layout is effectively the first layout in the\n                            // keymap, so use it for new windows.\n                            Cell::new(KeyboardLayout::default())\n                        });\n                        cell.get()\n                    });\n                }\n                if new_layout != current_layout && focus.surface().is_some() {\n                    keyboard.set_focus(self, None, SERIAL_COUNTER.next_serial());\n                    keyboard.with_xkb_state(self, |mut context| {\n                        context.set_layout(new_layout);\n                    });\n                }\n            }\n\n            self.niri.keyboard_focus.clone_from(&focus);\n            keyboard.set_focus(self, focus.into_surface(), SERIAL_COUNTER.next_serial());\n\n            // FIXME: can be more granular.\n            self.niri.queue_redraw_all();\n        }\n    }\n\n    /// Loads the xkb keymap from a file config setting.\n    fn set_xkb_file(&mut self, xkb_file: String) -> anyhow::Result<()> {\n        let xkb_file = PathBuf::from(xkb_file);\n        let xkb_file = expand_home(&xkb_file)\n            .context(\"failed to expand ~\")?\n            .unwrap_or(xkb_file);\n\n        let keymap = std::fs::read_to_string(xkb_file).context(\"failed to read xkb_file\")?;\n\n        let xkb = self.niri.seat.get_keyboard().unwrap();\n        xkb.set_keymap_from_string(self, keymap)\n            .context(\"failed to set keymap\")?;\n\n        Ok(())\n    }\n\n    fn load_xkb_file(&mut self) {\n        let xkb_file = self.niri.config.borrow().input.keyboard.xkb.file.clone();\n        if let Some(xkb_file) = xkb_file {\n            if let Err(err) = self.set_xkb_file(xkb_file) {\n                warn!(\"error loading xkb_file: {err:?}\");\n            }\n        }\n    }\n\n    pub fn set_xkb_config(&mut self, xkb: XkbConfig) {\n        let keyboard = self.niri.seat.get_keyboard().unwrap();\n        let num_lock = keyboard.modifier_state().num_lock;\n        if let Err(err) = keyboard.set_xkb_config(self, xkb) {\n            warn!(\"error updating xkb config: {err:?}\");\n            return;\n        }\n\n        // Restore num lock to its previous value.\n        let mut mods_state = keyboard.modifier_state();\n        if mods_state.num_lock != num_lock {\n            mods_state.num_lock = num_lock;\n            keyboard.set_modifier_state(mods_state);\n        }\n    }\n\n    pub fn reload_config(&mut self, config: Result<Config, ()>) {\n        let _span = tracy_client::span!(\"State::reload_config\");\n\n        let mut config = match config {\n            Ok(config) => config,\n            Err(()) => {\n                self.niri.config_error_notification.show();\n                self.niri.queue_redraw_all();\n\n                #[cfg(feature = \"dbus\")]\n                self.niri.a11y_announce_config_error();\n\n                return;\n            }\n        };\n\n        self.niri.config_error_notification.hide();\n\n        // Find & orphan removed named workspaces.\n        let mut removed_workspaces: Vec<String> = vec![];\n        for ws in &self.niri.config.borrow().workspaces {\n            if !config.workspaces.iter().any(|w| w.name == ws.name) {\n                removed_workspaces.push(ws.name.0.clone());\n            }\n        }\n        for name in removed_workspaces {\n            self.niri.layout.unname_workspace(&name);\n        }\n\n        self.niri.layout.update_config(&config);\n        for mapped in self.niri.mapped_layer_surfaces.values_mut() {\n            mapped.update_config(&config);\n        }\n\n        // Create new named workspaces.\n        for ws_config in &config.workspaces {\n            self.niri.layout.ensure_named_workspace(ws_config);\n        }\n\n        let rate = 1.0 / config.animations.slowdown.max(0.001);\n        self.niri.clock.set_rate(rate);\n        self.niri\n            .clock\n            .set_complete_instantly(config.animations.off);\n\n        *CHILD_ENV.write().unwrap() = mem::take(&mut config.environment);\n\n        let mut reload_xkb = None;\n        let mut libinput_config_changed = false;\n        let mut output_config_changed = false;\n        let mut preserved_output_config = None;\n        let mut window_rules_changed = false;\n        let mut layer_rules_changed = false;\n        let mut shaders_changed = false;\n        let mut cursor_inactivity_timeout_changed = false;\n        let mut recent_windows_changed = false;\n        let mut xwls_changed = false;\n        let mut old_config = self.niri.config.borrow_mut();\n\n        // Reload the cursor.\n        if config.cursor != old_config.cursor {\n            self.niri\n                .cursor_manager\n                .reload(&config.cursor.xcursor_theme, config.cursor.xcursor_size);\n            self.niri.cursor_texture_cache.clear();\n        }\n\n        // We need &mut self to reload the xkb config, so just store it here.\n        if config.input.keyboard.xkb != old_config.input.keyboard.xkb {\n            reload_xkb = Some(config.input.keyboard.xkb.clone());\n        }\n\n        // Reload the repeat info.\n        if config.input.keyboard.repeat_rate != old_config.input.keyboard.repeat_rate\n            || config.input.keyboard.repeat_delay != old_config.input.keyboard.repeat_delay\n        {\n            let keyboard = self.niri.seat.get_keyboard().unwrap();\n            keyboard.change_repeat_info(\n                config.input.keyboard.repeat_rate.into(),\n                config.input.keyboard.repeat_delay.into(),\n            );\n        }\n\n        if config.input.touchpad != old_config.input.touchpad\n            || config.input.mouse != old_config.input.mouse\n            || config.input.trackball != old_config.input.trackball\n            || config.input.trackpoint != old_config.input.trackpoint\n            || config.input.tablet != old_config.input.tablet\n            || config.input.touch != old_config.input.touch\n        {\n            libinput_config_changed = true;\n        }\n\n        let ignored_nodes_changed =\n            config.debug.ignored_drm_devices != old_config.debug.ignored_drm_devices;\n\n        if config.outputs != self.niri.config_file_output_config {\n            output_config_changed = true;\n            self.niri\n                .config_file_output_config\n                .clone_from(&config.outputs);\n        } else {\n            // Output config did not change from the last disk load, so we need to preserve the\n            // transient changes.\n            preserved_output_config = Some(mem::take(&mut old_config.outputs));\n        }\n\n        let binds_changed = config.binds != old_config.binds;\n        let new_mod_key = self.backend.mod_key(&config);\n        if new_mod_key != self.backend.mod_key(&old_config) || binds_changed {\n            self.niri\n                .hotkey_overlay\n                .on_hotkey_config_updated(new_mod_key);\n            self.niri.mods_with_mouse_binds = mods_with_mouse_binds(new_mod_key, &config.binds);\n            self.niri.mods_with_wheel_binds = mods_with_wheel_binds(new_mod_key, &config.binds);\n            self.niri.mods_with_finger_scroll_binds =\n                mods_with_finger_scroll_binds(new_mod_key, &config.binds);\n        }\n\n        if config.window_rules != old_config.window_rules {\n            window_rules_changed = true;\n        }\n\n        if config.layer_rules != old_config.layer_rules {\n            layer_rules_changed = true;\n        }\n\n        if config.animations.window_resize.custom_shader\n            != old_config.animations.window_resize.custom_shader\n        {\n            let src = config.animations.window_resize.custom_shader.as_deref();\n            self.backend.with_primary_renderer(|renderer| {\n                shaders::set_custom_resize_program(renderer, src);\n            });\n            shaders_changed = true;\n        }\n\n        if config.animations.window_close.custom_shader\n            != old_config.animations.window_close.custom_shader\n        {\n            let src = config.animations.window_close.custom_shader.as_deref();\n            self.backend.with_primary_renderer(|renderer| {\n                shaders::set_custom_close_program(renderer, src);\n            });\n            shaders_changed = true;\n        }\n\n        if config.animations.window_open.custom_shader\n            != old_config.animations.window_open.custom_shader\n        {\n            let src = config.animations.window_open.custom_shader.as_deref();\n            self.backend.with_primary_renderer(|renderer| {\n                shaders::set_custom_open_program(renderer, src);\n            });\n            shaders_changed = true;\n        }\n\n        if config.cursor.hide_after_inactive_ms != old_config.cursor.hide_after_inactive_ms {\n            cursor_inactivity_timeout_changed = true;\n        }\n\n        if config.debug.keep_laptop_panel_on_when_lid_is_closed\n            != old_config.debug.keep_laptop_panel_on_when_lid_is_closed\n        {\n            output_config_changed = true;\n        }\n\n        if config.debug.ignored_drm_devices != old_config.debug.ignored_drm_devices {\n            output_config_changed = true;\n        }\n\n        // FIXME: move backdrop rendering into layout::Monitor, then this will become unnecessary.\n        if config.overview.backdrop_color != old_config.overview.backdrop_color {\n            output_config_changed = true;\n        }\n        if config.layout.background_color != old_config.layout.background_color {\n            output_config_changed = true;\n        }\n\n        if config.recent_windows != old_config.recent_windows {\n            recent_windows_changed = true;\n        }\n\n        if config.xwayland_satellite != old_config.xwayland_satellite {\n            xwls_changed = true;\n        }\n\n        *old_config = config;\n\n        if let Some(outputs) = preserved_output_config {\n            old_config.outputs = outputs;\n        }\n\n        // Release the borrow.\n        drop(old_config);\n\n        // Now with a &mut self we can reload the xkb config.\n        if let Some(mut xkb) = reload_xkb {\n            let mut set_xkb_config = true;\n\n            // It's fine to .take() the xkb file, as this is a\n            // clone and the file field is not used in the XkbConfig.\n            if let Some(xkb_file) = xkb.file.take() {\n                if let Err(err) = self.set_xkb_file(xkb_file) {\n                    warn!(\"error reloading xkb_file: {err:?}\");\n                } else {\n                    // We successfully set xkb file so we don't need to fallback to XkbConfig.\n                    set_xkb_config = false;\n                }\n            }\n\n            if set_xkb_config {\n                // If xkb is unset in the niri config, use settings from locale1.\n                if xkb == Xkb::default() {\n                    trace!(\"using xkb from locale1\");\n                    xkb = self.niri.xkb_from_locale1.clone().unwrap_or_default();\n                }\n\n                self.set_xkb_config(xkb.to_xkb_config());\n            }\n\n            self.ipc_keyboard_layouts_changed();\n        }\n\n        if libinput_config_changed {\n            let config = self.niri.config.borrow();\n            for mut device in self.niri.devices.iter().cloned() {\n                apply_libinput_settings(&config.input, &mut device);\n            }\n        }\n\n        if ignored_nodes_changed {\n            self.backend.update_ignored_nodes_config(&mut self.niri);\n        }\n\n        if output_config_changed {\n            self.reload_output_config();\n        }\n\n        if window_rules_changed {\n            self.niri.recompute_window_rules();\n        }\n\n        if layer_rules_changed {\n            self.niri.recompute_layer_rules();\n        }\n\n        if shaders_changed {\n            self.niri.update_shaders();\n        }\n\n        if cursor_inactivity_timeout_changed {\n            // Force reset due to timeout change.\n            self.niri.pointer_inactivity_timer_got_reset = false;\n            self.niri.reset_pointer_inactivity_timer();\n        }\n\n        if binds_changed {\n            self.niri.window_mru_ui.update_binds();\n        }\n\n        if recent_windows_changed {\n            self.niri.window_mru_ui.update_config();\n        }\n\n        if xwls_changed {\n            // If xwl-s was previously working and is now off, we don't try to kill it or stop\n            // watching the sockets, for simplicity's sake.\n            let was_working = self.niri.satellite.is_some();\n\n            // Try to start, or restart in case the user corrected the path or something.\n            xwayland::satellite::setup(self);\n\n            let config = self.niri.config.borrow();\n            let display_name = (!config.xwayland_satellite.off)\n                .then_some(self.niri.satellite.as_ref())\n                .flatten()\n                .map(|satellite| satellite.display_name().to_owned());\n\n            if let Some(name) = &display_name {\n                if !was_working {\n                    info!(\"listening on X11 socket: {name}\");\n                }\n            }\n\n            // This won't change the systemd environment, but oh well.\n            *CHILD_DISPLAY.write().unwrap() = display_name;\n        }\n\n        // Can't really update xdg-decoration settings since we have to hide the globals for CSD\n        // due to the SDL2 bug... I don't imagine clients are prepared for the xdg-decoration\n        // global suddenly appearing? Either way, right now it's live-reloaded in a sense that new\n        // clients will use the new xdg-decoration setting.\n\n        self.niri.queue_redraw_all();\n    }\n\n    pub fn reload_output_config(&mut self) {\n        let mut resized_outputs = vec![];\n        let mut recolored_outputs = vec![];\n\n        for output in self.niri.global_space.outputs() {\n            let name = output.user_data().get::<OutputName>().unwrap();\n            let full_config = self.niri.config.borrow_mut();\n            let config = full_config.outputs.find(name);\n\n            let scale = config\n                .and_then(|c| c.scale)\n                .map(|s| s.0)\n                .unwrap_or_else(|| {\n                    let size_mm = output.physical_properties().size;\n                    let resolution = output.current_mode().unwrap().size;\n                    guess_monitor_scale(size_mm, resolution)\n                });\n            let scale = closest_representable_scale(scale.clamp(0.1, 10.));\n\n            let mut transform = panel_orientation(output)\n                + config\n                    .map(|c| ipc_transform_to_smithay(c.transform))\n                    .unwrap_or(Transform::Normal);\n            // FIXME: fix winit damage on other transforms.\n            if name.connector == \"winit\" {\n                transform = Transform::Flipped180;\n            }\n\n            if output.current_scale().fractional_scale() != scale\n                || output.current_transform() != transform\n            {\n                output.change_current_state(\n                    None,\n                    Some(transform),\n                    Some(output::Scale::Fractional(scale)),\n                    None,\n                );\n                self.niri.ipc_outputs_changed = true;\n                resized_outputs.push(output.clone());\n            }\n\n            let mut backdrop_color = config\n                .and_then(|c| c.backdrop_color)\n                .unwrap_or(full_config.overview.backdrop_color)\n                .to_array_unpremul();\n            backdrop_color[3] = 1.;\n            let backdrop_color = Color32F::from(backdrop_color);\n\n            if let Some(state) = self.niri.output_state.get_mut(output) {\n                if state.backdrop_buffer.color() != backdrop_color {\n                    state.backdrop_buffer.set_color(backdrop_color);\n                    recolored_outputs.push(output.clone());\n                }\n            }\n\n            for mon in self.niri.layout.monitors_mut() {\n                if mon.output() != output {\n                    continue;\n                }\n\n                let mut layout_config = config.and_then(|c| c.layout.clone());\n                // Support the deprecated non-layout background-color key.\n                if let Some(layout) = &mut layout_config {\n                    if layout.background_color.is_none() {\n                        layout.background_color = config.and_then(|c| c.background_color);\n                    }\n                }\n\n                if mon.update_layout_config(layout_config) {\n                    // Also redraw these; if anything, the background color could've changed.\n                    recolored_outputs.push(output.clone());\n                }\n                break;\n            }\n        }\n\n        for output in resized_outputs {\n            self.niri.output_resized(&output);\n        }\n\n        for output in recolored_outputs {\n            self.niri.queue_redraw(&output);\n        }\n\n        self.backend.on_output_config_changed(&mut self.niri);\n\n        self.niri.reposition_outputs(None);\n\n        if let Some(touch) = self.niri.seat.get_touch() {\n            touch.cancel(self);\n        }\n\n        let config = self.niri.config.borrow().outputs.clone();\n        self.niri.output_management_state.on_config_changed(config);\n    }\n\n    pub fn modify_output_config<F>(&mut self, name: &str, fun: F)\n    where\n        F: FnOnce(&mut niri_config::Output),\n    {\n        // Try hard to find the output config section corresponding to the output set by the\n        // user. Since if we add a new section and some existing section also matches the\n        // output, then our new section won't do anything.\n        let temp;\n        let match_name = if let Some(output) = self.niri.output_by_name_match(name) {\n            output.user_data().get::<OutputName>().unwrap()\n        } else if let Some(output_name) = self\n            .backend\n            .tty_checked()\n            .and_then(|tty| tty.disconnected_connector_name_by_name_match(name))\n        {\n            temp = output_name;\n            &temp\n        } else {\n            // Even if name is \"make model serial\", matching will work fine this way.\n            temp = OutputName {\n                connector: name.to_owned(),\n                make: None,\n                model: None,\n                serial: None,\n            };\n            &temp\n        };\n\n        let mut config = self.niri.config.borrow_mut();\n        let config = if let Some(config) = config.outputs.find_mut(match_name) {\n            config\n        } else {\n            config.outputs.0.push(niri_config::Output {\n                // Save name as set by the user.\n                name: String::from(name),\n                ..Default::default()\n            });\n            config.outputs.0.last_mut().unwrap()\n        };\n\n        fun(config);\n    }\n\n    pub fn apply_transient_output_config(&mut self, name: &str, action: niri_ipc::OutputAction) {\n        self.modify_output_config(name, move |config| match action {\n            niri_ipc::OutputAction::Off => config.off = true,\n            niri_ipc::OutputAction::On => config.off = false,\n            niri_ipc::OutputAction::Mode { mode } => {\n                config.mode = match mode {\n                    niri_ipc::ModeToSet::Automatic => None,\n                    niri_ipc::ModeToSet::Specific(mode) => Some(niri_config::output::Mode {\n                        custom: false,\n                        mode,\n                    }),\n                };\n                config.modeline = None;\n            }\n            niri_ipc::OutputAction::CustomMode { mode } => {\n                config.mode = Some(niri_config::output::Mode { custom: true, mode });\n                config.modeline = None;\n            }\n            niri_ipc::OutputAction::Modeline {\n                clock,\n                hdisplay,\n                hsync_start,\n                hsync_end,\n                htotal,\n                vdisplay,\n                vsync_start,\n                vsync_end,\n                vtotal,\n                hsync_polarity,\n                vsync_polarity,\n            } => {\n                // Do not reset config.mode to None since it's used as a fallback.\n                config.modeline = Some(niri_config::output::Modeline {\n                    clock,\n                    hdisplay,\n                    hsync_start,\n                    hsync_end,\n                    htotal,\n                    vdisplay,\n                    vsync_start,\n                    vsync_end,\n                    vtotal,\n                    hsync_polarity,\n                    vsync_polarity,\n                })\n            }\n            niri_ipc::OutputAction::Scale { scale } => {\n                config.scale = match scale {\n                    niri_ipc::ScaleToSet::Automatic => None,\n                    niri_ipc::ScaleToSet::Specific(scale) => Some(FloatOrInt(scale)),\n                }\n            }\n            niri_ipc::OutputAction::Transform { transform } => config.transform = transform,\n            niri_ipc::OutputAction::Position { position } => {\n                config.position = match position {\n                    niri_ipc::PositionToSet::Automatic => None,\n                    niri_ipc::PositionToSet::Specific(position) => Some(niri_config::Position {\n                        x: position.x,\n                        y: position.y,\n                    }),\n                }\n            }\n            niri_ipc::OutputAction::Vrr { vrr } => {\n                config.variable_refresh_rate = if vrr.vrr {\n                    Some(niri_config::Vrr {\n                        on_demand: vrr.on_demand,\n                    })\n                } else {\n                    None\n                }\n            }\n        });\n\n        self.reload_output_config();\n    }\n\n    pub fn refresh_ipc_outputs(&mut self) {\n        if !self.niri.ipc_outputs_changed {\n            return;\n        }\n        self.niri.ipc_outputs_changed = false;\n\n        let _span = tracy_client::span!(\"State::refresh_ipc_outputs\");\n\n        for ipc_output in self.backend.ipc_outputs().lock().unwrap().values_mut() {\n            let logical = self\n                .niri\n                .global_space\n                .outputs()\n                .find(|output| output.name() == ipc_output.name)\n                .map(logical_output);\n            ipc_output.logical = logical;\n        }\n\n        #[cfg(feature = \"dbus\")]\n        self.niri.on_ipc_outputs_changed();\n\n        let new_config = self.backend.ipc_outputs().lock().unwrap().clone();\n        self.niri.output_management_state.notify_changes(new_config);\n    }\n\n    pub fn open_screenshot_ui(&mut self, show_pointer: bool, path: Option<String>) {\n        if self.niri.is_locked() || self.niri.screenshot_ui.is_open() {\n            return;\n        }\n\n        let default_output = self\n            .niri\n            .output_under_cursor()\n            .or_else(|| self.niri.layout.active_output().cloned());\n        let Some(default_output) = default_output else {\n            return;\n        };\n\n        self.niri.update_render_elements(None);\n\n        let Some(screenshots) = self\n            .backend\n            .with_primary_renderer(|renderer| self.niri.capture_screenshots(renderer).collect())\n        else {\n            return;\n        };\n\n        // Now that we captured the screenshots, clear grabs like drag-and-drop, etc.\n        self.niri.seat.get_pointer().unwrap().unset_grab(\n            self,\n            SERIAL_COUNTER.next_serial(),\n            get_monotonic_time().as_millis() as u32,\n        );\n        if let Some(touch) = self.niri.seat.get_touch() {\n            touch.unset_grab(self);\n        }\n\n        self.backend.with_primary_renderer(|renderer| {\n            self.niri\n                .screenshot_ui\n                .open(renderer, screenshots, default_output, show_pointer, path)\n        });\n\n        self.niri\n            .cursor_manager\n            .set_cursor_image(CursorImageStatus::Named(CursorIcon::Crosshair));\n        self.niri.queue_redraw_all();\n    }\n\n    pub fn handle_pick_color(&mut self, tx: async_channel::Sender<Option<niri_ipc::PickedColor>>) {\n        let pointer = self.niri.seat.get_pointer().unwrap();\n        let start_data = PointerGrabStartData {\n            focus: None,\n            button: 0,\n            location: pointer.current_location(),\n        };\n        let grab = PickColorGrab::new(start_data);\n        pointer.set_grab(self, grab, SERIAL_COUNTER.next_serial(), Focus::Clear);\n        self.niri.pick_color = Some(tx);\n        self.niri\n            .cursor_manager\n            .set_cursor_image(CursorImageStatus::Named(CursorIcon::Crosshair));\n        self.niri.queue_redraw_all();\n    }\n\n    pub fn confirm_screenshot(&mut self, write_to_disk: bool) {\n        let ScreenshotUi::Open { path, .. } = &mut self.niri.screenshot_ui else {\n            return;\n        };\n        let path = path.take();\n\n        self.backend.with_primary_renderer(|renderer| {\n            match self.niri.screenshot_ui.capture(renderer) {\n                Ok((size, pixels)) => {\n                    if let Err(err) = self.niri.save_screenshot(size, pixels, write_to_disk, path) {\n                        warn!(\"error saving screenshot: {err:?}\");\n                    }\n                }\n                Err(err) => {\n                    warn!(\"error capturing screenshot: {err:?}\");\n                }\n            }\n        });\n\n        self.niri.screenshot_ui.close();\n        self.niri\n            .cursor_manager\n            .set_cursor_image(CursorImageStatus::default_named());\n        self.niri.queue_redraw_all();\n    }\n\n    #[cfg(not(feature = \"xdp-gnome-screencast\"))]\n    pub fn set_dynamic_cast_target(&mut self, _target: CastTarget) {}\n\n    #[cfg(feature = \"dbus\")]\n    pub fn on_screen_shot_msg(\n        &mut self,\n        to_screenshot: &async_channel::Sender<NiriToScreenshot>,\n        msg: ScreenshotToNiri,\n    ) {\n        match msg {\n            ScreenshotToNiri::TakeScreenshot { include_cursor } => {\n                self.handle_take_screenshot(to_screenshot, include_cursor);\n            }\n            ScreenshotToNiri::PickColor(tx) => {\n                self.handle_pick_color(tx);\n            }\n        }\n    }\n\n    #[cfg(feature = \"dbus\")]\n    fn handle_take_screenshot(\n        &mut self,\n        to_screenshot: &async_channel::Sender<NiriToScreenshot>,\n        include_cursor: bool,\n    ) {\n        let _span = tracy_client::span!(\"TakeScreenshot\");\n\n        let rv = self.backend.with_primary_renderer(|renderer| {\n            let on_done = {\n                let to_screenshot = to_screenshot.clone();\n                move |path| {\n                    let msg = NiriToScreenshot::ScreenshotResult(Some(path));\n                    if let Err(err) = to_screenshot.send_blocking(msg) {\n                        warn!(\"error sending path to screenshot: {err:?}\");\n                    }\n                }\n            };\n\n            let res = self\n                .niri\n                .screenshot_all_outputs(renderer, include_cursor, on_done);\n\n            if let Err(err) = res {\n                warn!(\"error taking a screenshot: {err:?}\");\n\n                let msg = NiriToScreenshot::ScreenshotResult(None);\n                if let Err(err) = to_screenshot.send_blocking(msg) {\n                    warn!(\"error sending None to screenshot: {err:?}\");\n                }\n            }\n        });\n\n        if rv.is_none() {\n            let msg = NiriToScreenshot::ScreenshotResult(None);\n            if let Err(err) = to_screenshot.send_blocking(msg) {\n                warn!(\"error sending None to screenshot: {err:?}\");\n            }\n        }\n    }\n\n    #[cfg(feature = \"dbus\")]\n    pub fn on_introspect_msg(\n        &mut self,\n        to_introspect: &async_channel::Sender<NiriToIntrospect>,\n        msg: IntrospectToNiri,\n    ) {\n        use crate::utils::with_toplevel_role;\n\n        let IntrospectToNiri::GetWindows = msg;\n        let _span = tracy_client::span!(\"GetWindows\");\n\n        let mut windows = HashMap::new();\n\n        #[cfg(feature = \"xdp-gnome-screencast\")]\n        windows.insert(\n            self.niri.casting.dynamic_cast_id_for_portal.get(),\n            gnome_shell_introspect::WindowProperties {\n                title: String::from(\"niri Dynamic Cast Target\"),\n                app_id: String::from(\"rs.bxt.niri.desktop\"),\n            },\n        );\n\n        self.niri.layout.with_windows(|mapped, _, _, _| {\n            let id = mapped.id().get();\n            let props = with_toplevel_role(mapped.toplevel(), |role| {\n                gnome_shell_introspect::WindowProperties {\n                    title: role.title.clone().unwrap_or_default(),\n                    app_id: role\n                        .app_id\n                        .as_ref()\n                        // We don't do proper .desktop file tracking (it's quite involved), and\n                        // Wayland windows can set any app id they want. However, this seems to\n                        // work well enough in practice.\n                        .map(|app_id| format!(\"{app_id}.desktop\"))\n                        .unwrap_or_default(),\n                }\n            });\n\n            windows.insert(id, props);\n        });\n\n        let msg = NiriToIntrospect::Windows(windows);\n        if let Err(err) = to_introspect.send_blocking(msg) {\n            warn!(\"error sending windows to introspect: {err:?}\");\n        }\n    }\n\n    #[cfg(feature = \"dbus\")]\n    pub fn on_login1_msg(&mut self, msg: Login1ToNiri) {\n        let Login1ToNiri::LidClosedChanged(is_closed) = msg;\n\n        trace!(\"login1 lid {}\", if is_closed { \"closed\" } else { \"opened\" });\n        self.set_lid_closed(is_closed);\n    }\n\n    #[cfg(feature = \"dbus\")]\n    pub fn on_locale1_msg(&mut self, msg: Locale1ToNiri) {\n        let Locale1ToNiri::XkbChanged(xkb) = msg;\n\n        trace!(\"locale1 xkb settings changed: {xkb:?}\");\n        let xkb = self.niri.xkb_from_locale1.insert(xkb);\n\n        {\n            let config = self.niri.config.borrow();\n            if config.input.keyboard.xkb != Xkb::default() {\n                trace!(\"ignoring locale1 xkb change because niri config has xkb settings\");\n                return;\n            }\n        }\n\n        let xkb = xkb.clone();\n        self.set_xkb_config(xkb.to_xkb_config());\n        self.ipc_keyboard_layouts_changed();\n    }\n}\n\nimpl Niri {\n    pub fn new(\n        config: Rc<RefCell<Config>>,\n        event_loop: LoopHandle<'static, State>,\n        stop_signal: LoopSignal,\n        display: Display<State>,\n        backend: &Backend,\n        create_wayland_socket: bool,\n        is_session_instance: bool,\n    ) -> Self {\n        let _span = tracy_client::span!(\"Niri::new\");\n\n        let (executor, scheduler) = calloop::futures::executor().unwrap();\n        event_loop.insert_source(executor, |_, _, _| ()).unwrap();\n\n        let display_handle = display.handle();\n        let config_ = config.borrow();\n        let config_file_output_config = config_.outputs.clone();\n\n        let mut animation_clock = Clock::default();\n\n        let rate = 1.0 / config_.animations.slowdown.max(0.001);\n        animation_clock.set_rate(rate);\n        animation_clock.set_complete_instantly(config_.animations.off);\n\n        let layout = Layout::new(animation_clock.clone(), &config_);\n\n        let (blocker_cleared_tx, blocker_cleared_rx) = mpsc::channel();\n\n        fn client_is_unrestricted(client: &Client) -> bool {\n            !client.get_data::<ClientState>().unwrap().restricted\n        }\n\n        let compositor_state = CompositorState::new_v6::<State>(&display_handle);\n        let xdg_shell_state = XdgShellState::new_with_capabilities::<State>(\n            &display_handle,\n            [WmCapabilities::Fullscreen, WmCapabilities::Maximize],\n        );\n        let xdg_decoration_state =\n            XdgDecorationState::new_with_filter::<State, _>(&display_handle, |client| {\n                client\n                    .get_data::<ClientState>()\n                    .unwrap()\n                    .can_view_decoration_globals\n            });\n        let kde_decoration_state = KdeDecorationState::new_with_filter::<State, _>(\n            &display_handle,\n            // If we want CSD we will hide the global.\n            KdeDecorationsMode::Server,\n            |client| {\n                client\n                    .get_data::<ClientState>()\n                    .unwrap()\n                    .can_view_decoration_globals\n            },\n        );\n        let layer_shell_state = WlrLayerShellState::new_with_filter::<State, _>(\n            &display_handle,\n            client_is_unrestricted,\n        );\n        let session_lock_state =\n            SessionLockManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        let shm_state = ShmState::new::<State>(\n            &display_handle,\n            vec![wl_shm::Format::Xbgr8888, wl_shm::Format::Abgr8888],\n        );\n        let output_manager_state =\n            OutputManagerState::new_with_xdg_output::<State>(&display_handle);\n        let dmabuf_state = DmabufState::new();\n        let fractional_scale_manager_state =\n            FractionalScaleManagerState::new::<State>(&display_handle);\n        let mut seat_state = SeatState::new();\n        let tablet_state = TabletManagerState::new::<State>(&display_handle);\n        let pointer_gestures_state = PointerGesturesState::new::<State>(&display_handle);\n        let relative_pointer_state = RelativePointerManagerState::new::<State>(&display_handle);\n        let pointer_constraints_state = PointerConstraintsState::new::<State>(&display_handle);\n        let idle_notifier_state = IdleNotifierState::new(&display_handle, event_loop.clone());\n        let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(&display_handle);\n        let data_device_state = DataDeviceState::new::<State>(&display_handle);\n        let primary_selection_state =\n            PrimarySelectionState::new_with_filter::<State, _>(&display_handle, |client| {\n                !client\n                    .get_data::<ClientState>()\n                    .unwrap()\n                    .primary_selection_disabled\n            });\n        let wlr_data_control_state = WlrDataControlState::new::<State, _>(\n            &display_handle,\n            Some(&primary_selection_state),\n            client_is_unrestricted,\n        );\n        let ext_data_control_state = ExtDataControlState::new::<State, _>(\n            &display_handle,\n            Some(&primary_selection_state),\n            client_is_unrestricted,\n        );\n        let presentation_state =\n            PresentationState::new::<State>(&display_handle, Monotonic::ID as u32);\n        let security_context_state =\n            SecurityContextState::new::<State, _>(&display_handle, client_is_unrestricted);\n\n        let text_input_state = TextInputManagerState::new::<State>(&display_handle);\n        let input_method_state =\n            InputMethodManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        let keyboard_shortcuts_inhibit_state =\n            KeyboardShortcutsInhibitState::new::<State>(&display_handle);\n        let virtual_keyboard_state =\n            VirtualKeyboardManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        let virtual_pointer_state =\n            VirtualPointerManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        let foreign_toplevel_state =\n            ForeignToplevelManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        let ext_workspace_state =\n            ExtWorkspaceManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        let mut output_management_state =\n            OutputManagementManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        output_management_state.on_config_changed(config_.outputs.clone());\n        let screencopy_state =\n            ScreencopyManagerState::new::<State, _>(&display_handle, client_is_unrestricted);\n        let viewporter_state = ViewporterState::new::<State>(&display_handle);\n        let xdg_foreign_state = XdgForeignState::new::<State>(&display_handle);\n\n        let is_tty = matches!(backend, Backend::Tty(_));\n        let gamma_control_manager_state =\n            GammaControlManagerState::new::<State, _>(&display_handle, move |client| {\n                is_tty && !client.get_data::<ClientState>().unwrap().restricted\n            });\n        let activation_state = XdgActivationState::new::<State>(&display_handle);\n        event_loop\n            .insert_source(\n                Timer::from_duration(XDG_ACTIVATION_TOKEN_TIMEOUT),\n                |_, _, state| {\n                    state.niri.activation_state.retain_tokens(|_, token_data| {\n                        token_data.timestamp.elapsed() < XDG_ACTIVATION_TOKEN_TIMEOUT\n                    });\n                    TimeoutAction::ToDuration(XDG_ACTIVATION_TOKEN_TIMEOUT)\n                },\n            )\n            .unwrap();\n\n        let mutter_x11_interop_state =\n            MutterX11InteropManagerState::new::<State, _>(&display_handle, move |_| true);\n\n        #[cfg(test)]\n        let single_pixel_buffer_state = SinglePixelBufferState::new::<State>(&display_handle);\n\n        let mut seat: Seat<State> = seat_state.new_wl_seat(&display_handle, backend.seat_name());\n        let keyboard = match seat.add_keyboard(\n            config_.input.keyboard.xkb.to_xkb_config(),\n            config_.input.keyboard.repeat_delay.into(),\n            config_.input.keyboard.repeat_rate.into(),\n        ) {\n            Err(err) => {\n                if let smithay::input::keyboard::Error::BadKeymap = err {\n                    warn!(\"error loading the configured xkb keymap, trying default\");\n                } else {\n                    warn!(\"error adding keyboard: {err:?}\");\n                }\n                seat.add_keyboard(\n                    Default::default(),\n                    config_.input.keyboard.repeat_delay.into(),\n                    config_.input.keyboard.repeat_rate.into(),\n                )\n                .unwrap()\n            }\n            Ok(keyboard) => keyboard,\n        };\n        if config_.input.keyboard.numlock {\n            let mut modifier_state = keyboard.modifier_state();\n            modifier_state.num_lock = true;\n            keyboard.set_modifier_state(modifier_state);\n        }\n        seat.add_pointer();\n\n        let cursor_shape_manager_state = CursorShapeManagerState::new::<State>(&display_handle);\n        let cursor_manager =\n            CursorManager::new(&config_.cursor.xcursor_theme, config_.cursor.xcursor_size);\n\n        let mod_key = backend.mod_key(&config.borrow());\n        let mods_with_mouse_binds = mods_with_mouse_binds(mod_key, &config_.binds);\n        let mods_with_wheel_binds = mods_with_wheel_binds(mod_key, &config_.binds);\n        let mods_with_finger_scroll_binds = mods_with_finger_scroll_binds(mod_key, &config_.binds);\n\n        let screenshot_ui = ScreenshotUi::new(animation_clock.clone(), config.clone());\n        let window_mru_ui = WindowMruUi::new(config.clone());\n        let config_error_notification =\n            ConfigErrorNotification::new(animation_clock.clone(), config.clone());\n\n        let mut hotkey_overlay = HotkeyOverlay::new(config.clone(), mod_key);\n        if !config_.hotkey_overlay.skip_at_startup {\n            hotkey_overlay.show();\n        }\n\n        let exit_confirm_dialog = ExitConfirmDialog::new(animation_clock.clone(), config.clone());\n\n        #[cfg(feature = \"dbus\")]\n        let a11y = A11y::new(event_loop.clone());\n\n        event_loop\n            .insert_source(\n                Timer::from_duration(Duration::from_secs(1)),\n                |_, _, state| {\n                    state.niri.send_frame_callbacks_on_fallback_timer();\n                    TimeoutAction::ToDuration(Duration::from_secs(1))\n                },\n            )\n            .unwrap();\n\n        let socket_name = create_wayland_socket.then(|| {\n            let socket_source = ListeningSocketSource::new_auto().unwrap();\n            let socket_name = socket_source.socket_name().to_os_string();\n            event_loop\n                .insert_source(socket_source, move |client, _, state| {\n                    state.niri.insert_client(NewClient {\n                        client,\n                        restricted: false,\n                        credentials_unknown: false,\n                    });\n                })\n                .unwrap();\n            socket_name\n        });\n\n        let ipc_server = match IpcServer::start(&event_loop, socket_name.as_deref()) {\n            Ok(server) => Some(server),\n            Err(err) => {\n                warn!(\"error starting IPC server: {err:?}\");\n                None\n            }\n        };\n\n        #[cfg(feature = \"xdp-gnome-screencast\")]\n        let screencasting = Screencasting::new(&event_loop);\n\n        let display_source = Generic::new(display, Interest::READ, Mode::Level);\n        event_loop\n            .insert_source(display_source, |_, display, state| {\n                // SAFETY: we don't drop the display.\n                unsafe {\n                    display.get_mut().dispatch_clients(state).unwrap();\n                }\n                Ok(PostAction::Continue)\n            })\n            .unwrap();\n\n        event_loop\n            .insert_source(\n                Timer::from_duration(Duration::from_secs(60)),\n                |_, _, state| {\n                    let _span = tracy_client::span!(\"startup timeout\");\n                    state.niri.is_at_startup = false;\n                    state.niri.recompute_window_rules();\n                    state.niri.recompute_layer_rules();\n                    TimeoutAction::Drop\n                },\n            )\n            .unwrap();\n\n        drop(config_);\n        let mut niri = Self {\n            config,\n            config_file_output_config,\n            config_file_watcher: None,\n\n            event_loop,\n            scheduler,\n            stop_signal,\n            socket_name,\n            display_handle,\n            is_session_instance,\n            start_time: Instant::now(),\n            is_at_startup: true,\n            clock: animation_clock,\n\n            layout,\n            global_space: Space::default(),\n            sorted_outputs: Vec::default(),\n            output_state: HashMap::new(),\n            unmapped_windows: HashMap::new(),\n            unmapped_layer_surfaces: HashSet::new(),\n            mapped_layer_surfaces: HashMap::new(),\n            root_surface: HashMap::new(),\n            dmabuf_pre_commit_hook: HashMap::new(),\n            blocker_cleared_tx,\n            blocker_cleared_rx,\n            monitors_active: true,\n            is_lid_closed: false,\n\n            devices: HashSet::new(),\n            tablets: HashMap::new(),\n            touch: HashSet::new(),\n\n            compositor_state,\n            xdg_shell_state,\n            xdg_decoration_state,\n            kde_decoration_state,\n            layer_shell_state,\n            session_lock_state,\n            foreign_toplevel_state,\n            ext_workspace_state,\n            output_management_state,\n            screencopy_state,\n            viewporter_state,\n            xdg_foreign_state,\n            text_input_state,\n            input_method_state,\n            keyboard_shortcuts_inhibit_state,\n            virtual_keyboard_state,\n            virtual_pointer_state,\n            shm_state,\n            output_manager_state,\n            dmabuf_state,\n            fractional_scale_manager_state,\n            seat_state,\n            tablet_state,\n            pointer_gestures_state,\n            relative_pointer_state,\n            pointer_constraints_state,\n            idle_notifier_state,\n            idle_inhibit_manager_state,\n            data_device_state,\n            primary_selection_state,\n            wlr_data_control_state,\n            ext_data_control_state,\n            popups: PopupManager::default(),\n            popup_grab: None,\n            suppressed_keys: HashSet::new(),\n            suppressed_buttons: HashSet::new(),\n            bind_cooldown_timers: HashMap::new(),\n            bind_repeat_timer: Option::default(),\n            presentation_state,\n            security_context_state,\n            gamma_control_manager_state,\n            activation_state,\n            mutter_x11_interop_state,\n            #[cfg(test)]\n            single_pixel_buffer_state,\n\n            seat,\n            keyboard_focus: KeyboardFocus::Layout { surface: None },\n            layer_shell_on_demand_focus: None,\n            idle_inhibiting_surfaces: HashSet::new(),\n            is_fdo_idle_inhibited: Arc::new(AtomicBool::new(false)),\n            keyboard_shortcuts_inhibiting_surfaces: HashMap::new(),\n            xkb_from_locale1: None,\n            cursor_manager,\n            cursor_texture_cache: Default::default(),\n            cursor_shape_manager_state,\n            dnd_icon: None,\n            pointer_contents: PointContents::default(),\n            pointer_visibility: PointerVisibility::Visible,\n            pointer_inactivity_timer: None,\n            pointer_inactivity_timer_got_reset: false,\n            notified_activity_this_iteration: false,\n            pointer_inside_hot_corner: false,\n            tablet_cursor_location: None,\n            gesture_swipe_3f_cumulative: None,\n            overview_scroll_swipe_gesture: ScrollSwipeGesture::new(),\n            vertical_wheel_tracker: ScrollTracker::new(120),\n            horizontal_wheel_tracker: ScrollTracker::new(120),\n            mods_with_mouse_binds,\n            mods_with_wheel_binds,\n\n            // 10 is copied from Clutter: DISCRETE_SCROLL_STEP.\n            vertical_finger_scroll_tracker: ScrollTracker::new(10),\n            horizontal_finger_scroll_tracker: ScrollTracker::new(10),\n            mods_with_finger_scroll_binds,\n\n            lock_state: LockState::Unlocked,\n            locked_hint: None,\n\n            screenshot_ui,\n            config_error_notification,\n            hotkey_overlay,\n            exit_confirm_dialog,\n\n            window_mru_ui,\n            pending_mru_commit: None,\n\n            pick_window: None,\n            pick_color: None,\n\n            debug_draw_opaque_regions: false,\n            debug_draw_damage: false,\n\n            #[cfg(feature = \"dbus\")]\n            dbus: None,\n            #[cfg(feature = \"dbus\")]\n            a11y_keyboard_monitor: None,\n            #[cfg(feature = \"dbus\")]\n            a11y,\n            #[cfg(feature = \"dbus\")]\n            inhibit_power_key_fd: None,\n\n            ipc_server,\n            ipc_outputs_changed: false,\n\n            satellite: None,\n\n            #[cfg(feature = \"xdp-gnome-screencast\")]\n            casting: screencasting,\n        };\n\n        niri.reset_pointer_inactivity_timer();\n\n        niri\n    }\n\n    pub fn insert_client(&mut self, client: NewClient) {\n        let NewClient {\n            client,\n            restricted,\n            credentials_unknown,\n        } = client;\n\n        let config = self.config.borrow();\n        let data = Arc::new(ClientState {\n            compositor_state: Default::default(),\n            can_view_decoration_globals: config.prefer_no_csd,\n            primary_selection_disabled: config.clipboard.disable_primary,\n            restricted,\n            credentials_unknown,\n        });\n\n        if let Err(err) = self.display_handle.insert_client(client, data) {\n            warn!(\"error inserting client: {err}\");\n        }\n    }\n\n    #[cfg(feature = \"dbus\")]\n    pub fn inhibit_power_key(&mut self) -> anyhow::Result<()> {\n        use smithay::reexports::rustix::io::{fcntl_setfd, FdFlags};\n\n        let conn = zbus::blocking::Connection::system()?;\n\n        let message = conn.call_method(\n            Some(\"org.freedesktop.login1\"),\n            \"/org/freedesktop/login1\",\n            Some(\"org.freedesktop.login1.Manager\"),\n            \"Inhibit\",\n            &(\"handle-power-key\", \"niri\", \"Power key handling\", \"block\"),\n        )?;\n\n        let fd: zbus::zvariant::OwnedFd = message.body().deserialize()?;\n\n        // Don't leak the fd to child processes.\n        if let Err(err) = fcntl_setfd(&fd, FdFlags::CLOEXEC) {\n            warn!(\"error setting CLOEXEC on inhibit fd: {err:?}\");\n        };\n\n        self.inhibit_power_key_fd = Some(fd);\n\n        Ok(())\n    }\n\n    /// Repositions all outputs, optionally adding a new output.\n    pub fn reposition_outputs(&mut self, new_output: Option<&Output>) {\n        let _span = tracy_client::span!(\"Niri::reposition_outputs\");\n\n        #[derive(Debug)]\n        struct Data {\n            output: Output,\n            name: OutputName,\n            position: Option<Point<i32, Logical>>,\n            config: Option<niri_config::Position>,\n        }\n\n        let config = self.config.borrow();\n        let mut outputs = vec![];\n        for output in self.global_space.outputs().chain(new_output) {\n            let name = output.user_data().get::<OutputName>().unwrap();\n            let position = self.global_space.output_geometry(output).map(|geo| geo.loc);\n            let config = config.outputs.find(name).and_then(|c| c.position);\n\n            outputs.push(Data {\n                output: output.clone(),\n                name: name.clone(),\n                position,\n                config,\n            });\n        }\n        drop(config);\n\n        for Data { output, .. } in &outputs {\n            self.global_space.unmap_output(output);\n        }\n\n        // Connectors can appear in udev in any order. If we sort by name then we get output\n        // positioning that does not depend on the order they appeared.\n        //\n        // This sorting first compares by make/model/serial so that it is stable regardless of the\n        // connector name. However, if make/model/serial is equal or unknown, then it does fall\n        // back to comparing the connector name, which should always be unique.\n        outputs.sort_unstable_by(|a, b| a.name.compare(&b.name));\n\n        // Place all outputs with explicitly configured position first, then the unconfigured ones.\n        outputs.sort_by_key(|d| d.config.is_none());\n\n        trace!(\n            \"placing outputs in order: {:?}\",\n            outputs.iter().map(|d| &d.name.connector)\n        );\n\n        self.sorted_outputs = outputs\n            .iter()\n            .map(|Data { output, .. }| output.clone())\n            .collect();\n\n        for data in outputs.into_iter() {\n            let Data {\n                output,\n                name,\n                position,\n                config,\n            } = data;\n\n            let size = output_size(&output).to_i32_round();\n\n            let new_position = config\n                .map(|pos| Point::from((pos.x, pos.y)))\n                .filter(|pos| {\n                    // Ensure that the requested position does not overlap any existing output.\n                    let target_geom = Rectangle::new(*pos, size);\n\n                    let overlap = self\n                        .global_space\n                        .outputs()\n                        .map(|output| self.global_space.output_geometry(output).unwrap())\n                        .find(|geom| geom.overlaps(target_geom));\n\n                    if let Some(overlap) = overlap {\n                        warn!(\n                            \"output {} at x={} y={} sized {}x{} \\\n                             overlaps an existing output at x={} y={} sized {}x{}, \\\n                             falling back to automatic placement\",\n                            name.connector,\n                            pos.x,\n                            pos.y,\n                            size.w,\n                            size.h,\n                            overlap.loc.x,\n                            overlap.loc.y,\n                            overlap.size.w,\n                            overlap.size.h,\n                        );\n\n                        false\n                    } else {\n                        true\n                    }\n                })\n                .unwrap_or_else(|| {\n                    let x = self\n                        .global_space\n                        .outputs()\n                        .map(|output| self.global_space.output_geometry(output).unwrap())\n                        .map(|geom| geom.loc.x + geom.size.w)\n                        .max()\n                        .unwrap_or(0);\n\n                    Point::from((x, 0))\n                });\n\n            self.global_space.map_output(&output, new_position);\n\n            // By passing new_output as an Option, rather than mapping it into a bogus location\n            // in global_space, we ensure that this branch always runs for it.\n            if Some(new_position) != position {\n                debug!(\n                    \"putting output {} at x={} y={}\",\n                    name.connector, new_position.x, new_position.y\n                );\n                output.change_current_state(None, None, None, Some(new_position));\n                self.ipc_outputs_changed = true;\n                self.queue_redraw(&output);\n            }\n        }\n    }\n\n    pub fn add_output(&mut self, output: Output, refresh_interval: Option<Duration>, vrr: bool) {\n        let global = output.create_global::<State>(&self.display_handle);\n\n        let name = output.user_data().get::<OutputName>().unwrap();\n\n        let config = self.config.borrow();\n        let c = config.outputs.find(name);\n        let scale = c.and_then(|c| c.scale).map(|s| s.0).unwrap_or_else(|| {\n            let size_mm = output.physical_properties().size;\n            let resolution = output.current_mode().unwrap().size;\n            guess_monitor_scale(size_mm, resolution)\n        });\n        let scale = closest_representable_scale(scale.clamp(0.1, 10.));\n\n        let mut transform = panel_orientation(&output)\n            + c.map(|c| ipc_transform_to_smithay(c.transform))\n                .unwrap_or(Transform::Normal);\n\n        let mut backdrop_color = c\n            .and_then(|c| c.backdrop_color)\n            .unwrap_or(config.overview.backdrop_color)\n            .to_array_unpremul();\n        backdrop_color[3] = 1.;\n\n        // FIXME: fix winit damage on other transforms.\n        if name.connector == \"winit\" {\n            transform = Transform::Flipped180;\n        }\n\n        let mut layout_config = c.and_then(|c| c.layout.clone());\n        // Support the deprecated non-layout background-color key.\n        if let Some(layout) = &mut layout_config {\n            if layout.background_color.is_none() {\n                layout.background_color = c.and_then(|c| c.background_color);\n            }\n        }\n        drop(config);\n\n        // Set scale and transform before adding to the layout since that will read the output size.\n        output.change_current_state(\n            None,\n            Some(transform),\n            Some(output::Scale::Fractional(scale)),\n            None,\n        );\n\n        self.layout.add_output(output.clone(), layout_config);\n\n        let lock_render_state = if self.is_locked() {\n            // We haven't rendered anything yet so it's as good as locked.\n            LockRenderState::Locked\n        } else {\n            LockRenderState::Unlocked\n        };\n\n        let size = output_size(&output);\n        let state = OutputState {\n            global,\n            redraw_state: RedrawState::Idle,\n            on_demand_vrr_enabled: false,\n            unfinished_animations_remain: false,\n            frame_clock: FrameClock::new(refresh_interval, vrr),\n            last_drm_sequence: None,\n            vblank_throttle: VBlankThrottle::new(self.event_loop.clone(), name.connector.clone()),\n            frame_callback_sequence: 0,\n            backdrop_buffer: SolidColorBuffer::new(size, backdrop_color),\n            lock_render_state,\n            lock_surface: None,\n            lock_color_buffer: SolidColorBuffer::new(size, CLEAR_COLOR_LOCKED),\n            screen_transition: None,\n            debug_damage_tracker: OutputDamageTracker::from_output(&output),\n        };\n        let rv = self.output_state.insert(output.clone(), state);\n        assert!(rv.is_none(), \"output was already tracked\");\n\n        // Must be last since it will call queue_redraw(output) which needs things to be filled-in.\n        self.reposition_outputs(Some(&output));\n    }\n\n    pub fn remove_output(&mut self, output: &Output) {\n        for layer in layer_map_for_output(output).layers() {\n            layer.layer_surface().send_close();\n        }\n\n        self.layout.remove_output(output);\n        self.global_space.unmap_output(output);\n        self.reposition_outputs(None);\n        self.gamma_control_manager_state.output_removed(output);\n\n        let state = self.output_state.remove(output).unwrap();\n\n        match state.redraw_state {\n            RedrawState::Idle => (),\n            RedrawState::Queued => (),\n            RedrawState::WaitingForVBlank { .. } => (),\n            RedrawState::WaitingForEstimatedVBlank(token) => self.event_loop.remove(token),\n            RedrawState::WaitingForEstimatedVBlankAndQueued(token) => self.event_loop.remove(token),\n        }\n\n        self.stop_casts_for_target(CastTarget::output(output));\n        self.screencopy_state.remove_output(output);\n\n        // Disable the output global and remove some time later to give the clients some time to\n        // process it.\n        let global = state.global;\n        self.display_handle.disable_global::<State>(global.clone());\n        self.event_loop\n            .insert_source(\n                Timer::from_duration(Duration::from_secs(10)),\n                move |_, _, state| {\n                    state\n                        .niri\n                        .display_handle\n                        .remove_global::<State>(global.clone());\n                    TimeoutAction::Drop\n                },\n            )\n            .unwrap();\n\n        match mem::take(&mut self.lock_state) {\n            LockState::Locking(confirmation) => {\n                // We're locking and an output was removed, check if the requirements are now met.\n                let all_locked = self\n                    .output_state\n                    .values()\n                    .all(|state| state.lock_render_state == LockRenderState::Locked);\n\n                if all_locked {\n                    let lock = confirmation.ext_session_lock().clone();\n                    confirmation.lock();\n                    self.lock_state = LockState::Locked(lock);\n                } else {\n                    // Still waiting.\n                    self.lock_state = LockState::Locking(confirmation);\n                }\n            }\n            lock_state => {\n                self.lock_state = lock_state;\n                self.maybe_continue_to_locking();\n            }\n        }\n\n        if self.screenshot_ui.close() {\n            self.cursor_manager\n                .set_cursor_image(CursorImageStatus::default_named());\n            self.queue_redraw_all();\n        }\n\n        if self.window_mru_ui.output() == Some(output) {\n            self.cancel_mru();\n        }\n    }\n\n    pub fn output_resized(&mut self, output: &Output) {\n        let output_size = output_size(output);\n        let scale = output.current_scale();\n        let transform = output.current_transform();\n\n        {\n            let mut layer_map = layer_map_for_output(output);\n            for layer in layer_map.layers() {\n                layer.with_surfaces(|surface, data| {\n                    send_scale_transform(surface, data, scale, transform);\n                });\n\n                if let Some(mapped) = self.mapped_layer_surfaces.get_mut(layer) {\n                    mapped.update_sizes(output_size, scale.fractional_scale());\n                }\n            }\n            layer_map.arrange();\n        }\n\n        self.layout.update_output_size(output);\n\n        if let Some(state) = self.output_state.get_mut(output) {\n            state.backdrop_buffer.resize(output_size);\n\n            state.lock_color_buffer.resize(output_size);\n            if let Some(lock_surface) = &state.lock_surface {\n                configure_lock_surface(lock_surface, output);\n            }\n        }\n\n        // If the output size changed with an open screenshot UI, close the screenshot UI.\n        if let Some((old_size, old_scale, old_transform)) = self.screenshot_ui.output_size(output) {\n            let output_mode = output.current_mode().unwrap();\n            let size = transform.transform_size(output_mode.size);\n            let scale = output.current_scale().fractional_scale();\n            // FIXME: scale changes and transform flips shouldn't matter but they currently do since\n            // I haven't quite figured out how to draw the screenshot textures in\n            // physical coordinates.\n            if old_size != size || old_scale != scale || old_transform != transform {\n                self.screenshot_ui.close();\n                self.cursor_manager\n                    .set_cursor_image(CursorImageStatus::default_named());\n                self.queue_redraw_all();\n                return;\n            }\n        }\n\n        self.queue_redraw(output);\n    }\n\n    pub fn deactivate_monitors(&mut self, backend: &mut Backend) {\n        if !self.monitors_active {\n            return;\n        }\n\n        self.monitors_active = false;\n        backend.set_monitors_active(false);\n    }\n\n    pub fn activate_monitors(&mut self, backend: &mut Backend) {\n        if self.monitors_active {\n            return;\n        }\n\n        self.monitors_active = true;\n        backend.set_monitors_active(true);\n\n        self.queue_redraw_all();\n    }\n\n    pub fn output_under(&self, pos: Point<f64, Logical>) -> Option<(&Output, Point<f64, Logical>)> {\n        let output = self.global_space.output_under(pos).next()?;\n        let pos_within_output = pos\n            - self\n                .global_space\n                .output_geometry(output)\n                .unwrap()\n                .loc\n                .to_f64();\n\n        Some((output, pos_within_output))\n    }\n\n    fn is_inside_hot_corner(&self, output: &Output, pos: Point<f64, Logical>) -> bool {\n        let config = self.config.borrow();\n        let hot_corners = output\n            .user_data()\n            .get::<OutputName>()\n            .and_then(|name| config.outputs.find(name))\n            .and_then(|c| c.hot_corners)\n            .unwrap_or(config.gestures.hot_corners);\n\n        if hot_corners.off {\n            return false;\n        }\n\n        // Use size from the ceiled output geometry, since that's what we currently use for pointer\n        // motion clamping.\n        let geom = self.global_space.output_geometry(output).unwrap();\n        let size = geom.size.to_f64();\n\n        let contains = move |corner: Point<f64, Logical>| {\n            Rectangle::new(corner, Size::new(1., 1.)).contains(pos)\n        };\n\n        if hot_corners.top_right && contains(Point::new(size.w - 1., 0.)) {\n            return true;\n        }\n        if hot_corners.bottom_left && contains(Point::new(0., size.h - 1.)) {\n            return true;\n        }\n        if hot_corners.bottom_right && contains(Point::new(size.w - 1., size.h - 1.)) {\n            return true;\n        }\n\n        // If the user didn't explicitly set any corners, we default to top-left.\n        if (hot_corners.top_left\n            || !(hot_corners.top_right || hot_corners.bottom_right || hot_corners.bottom_left))\n            && contains(Point::new(0., 0.))\n        {\n            return true;\n        }\n\n        false\n    }\n\n    pub fn is_sticky_obscured_under(\n        &self,\n        output: &Output,\n        pos_within_output: Point<f64, Logical>,\n    ) -> bool {\n        // The ordering here must be consistent with the ordering in render() so that input is\n        // consistent with the visuals.\n\n        // Check if some layer-shell surface is on top.\n        let layers = layer_map_for_output(output);\n        let layer_surface_under = |layer, popup| {\n            layers\n                .layers_on(layer)\n                .rev()\n                .find_map(|layer| {\n                    let mapped = self.mapped_layer_surfaces.get(layer)?;\n\n                    let mut layer_pos_within_output =\n                        layers.layer_geometry(layer).unwrap().loc.to_f64();\n                    layer_pos_within_output += mapped.bob_offset();\n\n                    let surface_type = if popup {\n                        WindowSurfaceType::POPUP\n                    } else {\n                        WindowSurfaceType::TOPLEVEL\n                    } | WindowSurfaceType::SUBSURFACE;\n                    layer.surface_under(pos_within_output - layer_pos_within_output, surface_type)\n                })\n                .is_some()\n        };\n\n        let layer_toplevel_under = |layer| layer_surface_under(layer, false);\n        let layer_popup_under = |layer| layer_surface_under(layer, true);\n\n        if layer_popup_under(Layer::Overlay) || layer_toplevel_under(Layer::Overlay) {\n            return true;\n        }\n\n        let mon = self.layout.monitor_for_output(output).unwrap();\n        if mon.render_above_top_layer() {\n            return false;\n        }\n\n        if self.is_inside_hot_corner(output, pos_within_output) {\n            return true;\n        }\n\n        if layer_popup_under(Layer::Top) || layer_toplevel_under(Layer::Top) {\n            return true;\n        }\n\n        false\n    }\n\n    pub fn is_layout_obscured_under(\n        &self,\n        output: &Output,\n        pos_within_output: Point<f64, Logical>,\n    ) -> bool {\n        if self.layout.is_overview_open() {\n            return false;\n        }\n\n        // Check if some layer-shell surface is on top.\n        let layers = layer_map_for_output(output);\n        let layer_popup_under = |layer| {\n            layers\n                .layers_on(layer)\n                .rev()\n                .find_map(|layer_surface| {\n                    let mapped = self.mapped_layer_surfaces.get(layer_surface)?;\n                    if mapped.place_within_backdrop() {\n                        return None;\n                    }\n\n                    let mut layer_pos_within_output =\n                        layers.layer_geometry(layer_surface).unwrap().loc.to_f64();\n                    layer_pos_within_output += mapped.bob_offset();\n\n                    // Background and bottom layers move together with the workspaces.\n                    let mon = self.layout.monitor_for_output(output)?;\n                    let (_, geo) = mon.workspace_under(pos_within_output)?;\n                    layer_pos_within_output += geo.loc;\n\n                    let surface_type = WindowSurfaceType::POPUP | WindowSurfaceType::SUBSURFACE;\n                    layer_surface\n                        .surface_under(pos_within_output - layer_pos_within_output, surface_type)\n                })\n                .is_some()\n        };\n\n        if layer_popup_under(Layer::Bottom) || layer_popup_under(Layer::Background) {\n            return true;\n        }\n\n        false\n    }\n\n    /// Returns the workspace under the position to be activated.\n    ///\n    /// The return value is an output and a workspace index on it.\n    pub fn workspace_under(\n        &self,\n        extended_bounds: bool,\n        pos: Point<f64, Logical>,\n    ) -> Option<(Output, &Workspace<Mapped>)> {\n        if self.exit_confirm_dialog.is_open() || self.is_locked() || self.screenshot_ui.is_open() {\n            return None;\n        }\n\n        let (output, pos_within_output) = self.output_under(pos)?;\n\n        if self.is_sticky_obscured_under(output, pos_within_output) {\n            return None;\n        }\n\n        if self.is_layout_obscured_under(output, pos_within_output) {\n            return None;\n        }\n\n        let ws = self\n            .layout\n            .workspace_under(extended_bounds, output, pos_within_output)?;\n        Some((output.clone(), ws))\n    }\n\n    pub fn workspace_under_cursor(\n        &self,\n        extended_bounds: bool,\n    ) -> Option<(Output, &Workspace<Mapped>)> {\n        let pos = self.seat.get_pointer().unwrap().current_location();\n        self.workspace_under(extended_bounds, pos)\n    }\n\n    /// Returns the window under the position to be activated.\n    ///\n    /// The cursor may be inside the window's activation region, but not within the window's input\n    /// region.\n    pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<&Mapped> {\n        if self.exit_confirm_dialog.is_open()\n            || self.is_locked()\n            || self.screenshot_ui.is_open()\n            || self.window_mru_ui.is_open()\n        {\n            return None;\n        }\n\n        let (output, pos_within_output) = self.output_under(pos)?;\n\n        if self.is_sticky_obscured_under(output, pos_within_output) {\n            return None;\n        }\n\n        if let Some((window, _loc)) = self\n            .layout\n            .interactive_moved_window_under(output, pos_within_output)\n        {\n            return Some(window);\n        }\n\n        if self.is_layout_obscured_under(output, pos_within_output) {\n            return None;\n        }\n\n        let (window, _loc) = self.layout.window_under(output, pos_within_output)?;\n        Some(window)\n    }\n\n    /// Returns the window under the cursor to be activated.\n    ///\n    /// The cursor may be inside the window's activation region, but not within the window's input\n    /// region.\n    pub fn window_under_cursor(&self) -> Option<&Mapped> {\n        let pos = self.seat.get_pointer().unwrap().current_location();\n        self.window_under(pos)\n    }\n\n    /// Returns contents under the given point.\n    ///\n    /// We don't have a proper global space for all windows, so this function converts window\n    /// locations to global space according to where they are rendered.\n    ///\n    /// This function does not take pointer or touch grabs into account.\n    pub fn contents_under(&self, pos: Point<f64, Logical>) -> PointContents {\n        let mut rv = PointContents::default();\n\n        let Some((output, pos_within_output)) = self.output_under(pos) else {\n            return rv;\n        };\n        rv.output = Some(output.clone());\n        let output_pos_in_global_space = self.global_space.output_geometry(output).unwrap().loc;\n\n        // The ordering here must be consistent with the ordering in render() so that input is\n        // consistent with the visuals.\n\n        if self.exit_confirm_dialog.is_open() {\n            return rv;\n        } else if self.is_locked() {\n            let Some(state) = self.output_state.get(output) else {\n                return rv;\n            };\n            let Some(surface) = state.lock_surface.as_ref() else {\n                return rv;\n            };\n\n            rv.surface = under_from_surface_tree(\n                surface.wl_surface(),\n                pos_within_output,\n                // We put lock surfaces at (0, 0).\n                (0, 0),\n                WindowSurfaceType::ALL,\n            )\n            .map(|(surface, pos_within_output)| {\n                (\n                    surface,\n                    (pos_within_output + output_pos_in_global_space).to_f64(),\n                )\n            });\n\n            return rv;\n        }\n\n        if self.screenshot_ui.is_open() || self.window_mru_ui.is_open() {\n            return rv;\n        }\n\n        let layers = layer_map_for_output(output);\n        let layer_surface_under = |layer, popup| {\n            layers\n                .layers_on(layer)\n                .rev()\n                .find_map(|layer_surface| {\n                    let mapped = self.mapped_layer_surfaces.get(layer_surface)?;\n                    if mapped.place_within_backdrop() {\n                        return None;\n                    }\n\n                    let mut layer_pos_within_output =\n                        layers.layer_geometry(layer_surface).unwrap().loc.to_f64();\n                    layer_pos_within_output += mapped.bob_offset();\n\n                    // Background and bottom layers move together with the workspaces.\n                    if matches!(layer, Layer::Background | Layer::Bottom) {\n                        let mon = self.layout.monitor_for_output(output)?;\n                        let (_, geo) = mon.workspace_under(pos_within_output)?;\n                        layer_pos_within_output += geo.loc;\n                        // Don't need to deal with zoom here because in the overview background and\n                        // bottom layers don't receive input.\n                    }\n\n                    let surface_type = if popup {\n                        WindowSurfaceType::POPUP\n                    } else {\n                        WindowSurfaceType::TOPLEVEL\n                    } | WindowSurfaceType::SUBSURFACE;\n\n                    layer_surface\n                        .surface_under(pos_within_output - layer_pos_within_output, surface_type)\n                        .map(|(surface, pos_within_layer)| {\n                            (\n                                (surface, pos_within_layer.to_f64() + layer_pos_within_output),\n                                layer_surface,\n                            )\n                        })\n                })\n                .map(|(s, l)| (Some(s), (None, Some(l.clone()))))\n        };\n\n        let layer_toplevel_under = |layer| layer_surface_under(layer, false);\n        let layer_popup_under = |layer| layer_surface_under(layer, true);\n\n        let mapped_hit_data = |(mapped, hit): (&Mapped, HitType)| {\n            let window = &mapped.window;\n            let surface_and_pos = if let HitType::Input { win_pos } = hit {\n                let win_pos_within_output = win_pos;\n                window\n                    .surface_under(\n                        pos_within_output - win_pos_within_output,\n                        WindowSurfaceType::ALL,\n                    )\n                    .map(|(s, pos_within_window)| {\n                        (s, pos_within_window.to_f64() + win_pos_within_output)\n                    })\n            } else {\n                None\n            };\n            (surface_and_pos, (Some((window.clone(), hit)), None))\n        };\n\n        let interactive_moved_window_under = || {\n            self.layout\n                .interactive_moved_window_under(output, pos_within_output)\n                .map(mapped_hit_data)\n        };\n        let window_under = || {\n            self.layout\n                .window_under(output, pos_within_output)\n                .map(mapped_hit_data)\n        };\n\n        let mon = self.layout.monitor_for_output(output).unwrap();\n\n        let mut under =\n            layer_popup_under(Layer::Overlay).or_else(|| layer_toplevel_under(Layer::Overlay));\n\n        let is_overview_open = self.layout.is_overview_open();\n\n        // When rendering above the top layer, we put the regular monitor elements first.\n        // Otherwise, we will render all layer-shell pop-ups and the top layer on top.\n        if mon.render_above_top_layer() {\n            under = under\n                .or_else(interactive_moved_window_under)\n                .or_else(window_under)\n                .or_else(|| layer_popup_under(Layer::Top))\n                .or_else(|| layer_toplevel_under(Layer::Top))\n                .or_else(|| layer_popup_under(Layer::Bottom))\n                .or_else(|| layer_popup_under(Layer::Background))\n                .or_else(|| layer_toplevel_under(Layer::Bottom))\n                .or_else(|| layer_toplevel_under(Layer::Background));\n        } else {\n            if self.is_inside_hot_corner(output, pos_within_output) {\n                rv.hot_corner = true;\n                return rv;\n            }\n\n            under = under\n                .or_else(|| layer_popup_under(Layer::Top))\n                .or_else(|| layer_toplevel_under(Layer::Top));\n\n            under = under.or_else(interactive_moved_window_under);\n\n            if !is_overview_open {\n                under = under\n                    .or_else(|| layer_popup_under(Layer::Bottom))\n                    .or_else(|| layer_popup_under(Layer::Background));\n            }\n\n            under = under.or_else(window_under);\n\n            if !is_overview_open {\n                under = under\n                    .or_else(|| layer_toplevel_under(Layer::Bottom))\n                    .or_else(|| layer_toplevel_under(Layer::Background));\n            }\n        }\n\n        let Some((mut surface_and_pos, (window, layer))) = under else {\n            return rv;\n        };\n\n        if let Some((_, surface_pos)) = &mut surface_and_pos {\n            *surface_pos += output_pos_in_global_space.to_f64();\n        }\n\n        rv.surface = surface_and_pos;\n        rv.window = window;\n        rv.layer = layer;\n        rv\n    }\n\n    pub fn output_under_cursor(&self) -> Option<Output> {\n        let pos = self.seat.get_pointer().unwrap().current_location();\n        self.global_space.output_under(pos).next().cloned()\n    }\n\n    pub fn output_left_of(&self, current: &Output) -> Option<Output> {\n        let current_geo = self.global_space.output_geometry(current)?;\n        let extended_geo = Rectangle::new(\n            Point::from((i32::MIN / 2, current_geo.loc.y)),\n            Size::from((i32::MAX, current_geo.size.h)),\n        );\n\n        self.global_space\n            .outputs()\n            .map(|output| (output, self.global_space.output_geometry(output).unwrap()))\n            .filter(|(_, geo)| center(*geo).x < center(current_geo).x && geo.overlaps(extended_geo))\n            .min_by_key(|(_, geo)| center(current_geo).x - center(*geo).x)\n            .map(|(output, _)| output)\n            .cloned()\n    }\n\n    pub fn output_right_of(&self, current: &Output) -> Option<Output> {\n        let current_geo = self.global_space.output_geometry(current)?;\n        let extended_geo = Rectangle::new(\n            Point::from((i32::MIN / 2, current_geo.loc.y)),\n            Size::from((i32::MAX, current_geo.size.h)),\n        );\n\n        self.global_space\n            .outputs()\n            .map(|output| (output, self.global_space.output_geometry(output).unwrap()))\n            .filter(|(_, geo)| center(*geo).x > center(current_geo).x && geo.overlaps(extended_geo))\n            .min_by_key(|(_, geo)| center(*geo).x - center(current_geo).x)\n            .map(|(output, _)| output)\n            .cloned()\n    }\n\n    pub fn output_up_of(&self, current: &Output) -> Option<Output> {\n        let current_geo = self.global_space.output_geometry(current)?;\n        let extended_geo = Rectangle::new(\n            Point::from((current_geo.loc.x, i32::MIN / 2)),\n            Size::from((current_geo.size.w, i32::MAX)),\n        );\n\n        self.global_space\n            .outputs()\n            .map(|output| (output, self.global_space.output_geometry(output).unwrap()))\n            .filter(|(_, geo)| center(*geo).y < center(current_geo).y && geo.overlaps(extended_geo))\n            .min_by_key(|(_, geo)| center(current_geo).y - center(*geo).y)\n            .map(|(output, _)| output)\n            .cloned()\n    }\n\n    pub fn output_down_of(&self, current: &Output) -> Option<Output> {\n        let current_geo = self.global_space.output_geometry(current)?;\n        let extended_geo = Rectangle::new(\n            Point::from((current_geo.loc.x, i32::MIN / 2)),\n            Size::from((current_geo.size.w, i32::MAX)),\n        );\n\n        self.global_space\n            .outputs()\n            .map(|output| (output, self.global_space.output_geometry(output).unwrap()))\n            .filter(|(_, geo)| center(*geo).y > center(current_geo).y && geo.overlaps(extended_geo))\n            .min_by_key(|(_, geo)| center(*geo).y - center(current_geo).y)\n            .map(|(output, _)| output)\n            .cloned()\n    }\n\n    pub fn output_previous_of(&self, current: &Output) -> Option<Output> {\n        self.sorted_outputs\n            .iter()\n            .rev()\n            .skip_while(|&output| output != current)\n            .nth(1)\n            .or(self.sorted_outputs.last())\n            .filter(|&output| output != current)\n            .cloned()\n    }\n\n    pub fn output_next_of(&self, current: &Output) -> Option<Output> {\n        self.sorted_outputs\n            .iter()\n            .skip_while(|&output| output != current)\n            .nth(1)\n            .or(self.sorted_outputs.first())\n            .filter(|&output| output != current)\n            .cloned()\n    }\n\n    pub fn output_left(&self) -> Option<Output> {\n        let active = self.layout.active_output()?;\n        self.output_left_of(active)\n    }\n\n    pub fn output_right(&self) -> Option<Output> {\n        let active = self.layout.active_output()?;\n        self.output_right_of(active)\n    }\n\n    pub fn output_up(&self) -> Option<Output> {\n        let active = self.layout.active_output()?;\n        self.output_up_of(active)\n    }\n\n    pub fn output_down(&self) -> Option<Output> {\n        let active = self.layout.active_output()?;\n        self.output_down_of(active)\n    }\n\n    pub fn output_previous(&self) -> Option<Output> {\n        let active = self.layout.active_output()?;\n        self.output_previous_of(active)\n    }\n\n    pub fn output_next(&self) -> Option<Output> {\n        let active = self.layout.active_output()?;\n        self.output_next_of(active)\n    }\n\n    pub fn find_output_and_workspace_index(\n        &self,\n        workspace_reference: WorkspaceReference,\n    ) -> Option<(Option<Output>, usize)> {\n        let (target_workspace_index, target_workspace) = match workspace_reference {\n            WorkspaceReference::Index(index) => {\n                return Some((None, index.saturating_sub(1) as usize));\n            }\n            WorkspaceReference::Name(name) => self.layout.find_workspace_by_name(&name)?,\n            WorkspaceReference::Id(id) => {\n                let id = WorkspaceId::specific(id);\n                self.layout.find_workspace_by_id(id)?\n            }\n        };\n\n        let target_output = target_workspace.current_output();\n        Some((target_output.cloned(), target_workspace_index))\n    }\n\n    pub fn find_window_by_id(&self, id: MappedId) -> Option<Window> {\n        self.layout\n            .windows()\n            .find(|(_, m)| m.id() == id)\n            .map(|(_, m)| m.window.clone())\n    }\n\n    pub fn output_for_tablet(&self) -> Option<&Output> {\n        let config = self.config.borrow();\n        let map_to_output = config.input.tablet.map_to_output.as_ref();\n        map_to_output.and_then(|name| self.output_by_name_match(name))\n    }\n\n    pub fn output_for_touch(&self) -> Option<&Output> {\n        let config = self.config.borrow();\n        let map_to_output = config.input.touch.map_to_output.as_ref();\n        map_to_output\n            .and_then(|name| self.output_by_name_match(name))\n            .or_else(|| self.global_space.outputs().next())\n    }\n\n    pub fn output_by_name_match(&self, target: &str) -> Option<&Output> {\n        self.global_space\n            .outputs()\n            .find(|output| output_matches_name(output, target))\n    }\n\n    pub fn output_for_root(&self, root: &WlSurface) -> Option<&Output> {\n        // Check the main layout.\n        let win_out = self.layout.find_window_and_output(root);\n        let layout_output = win_out.map(|(_, output)| output);\n        if let Some(output) = layout_output {\n            return output;\n        }\n\n        // Check layer-shell.\n        let has_layer_surface = |o: &&Output| {\n            layer_map_for_output(o)\n                .layer_for_surface(root, WindowSurfaceType::TOPLEVEL)\n                .is_some()\n        };\n        self.layout.outputs().find(has_layer_surface)\n    }\n\n    pub fn lock_surface_focus(&self) -> Option<WlSurface> {\n        let output_under_cursor = self.output_under_cursor();\n        let output = output_under_cursor\n            .as_ref()\n            .or_else(|| self.layout.active_output())\n            .or_else(|| self.global_space.outputs().next())?;\n\n        let state = self.output_state.get(output)?;\n        state.lock_surface.as_ref().map(|s| s.wl_surface()).cloned()\n    }\n\n    /// Schedules an immediate redraw on all outputs if one is not already scheduled.\n    pub fn queue_redraw_all(&mut self) {\n        for state in self.output_state.values_mut() {\n            state.redraw_state = mem::take(&mut state.redraw_state).queue_redraw();\n        }\n    }\n\n    /// Schedules an immediate redraw if one is not already scheduled.\n    pub fn queue_redraw(&mut self, output: &Output) {\n        let state = self.output_state.get_mut(output).unwrap();\n        state.redraw_state = mem::take(&mut state.redraw_state).queue_redraw();\n    }\n\n    pub fn redraw_queued_outputs(&mut self, backend: &mut Backend) {\n        let _span = tracy_client::span!(\"Niri::redraw_queued_outputs\");\n\n        while let Some((output, _)) = self.output_state.iter().find(|(_, state)| {\n            matches!(\n                state.redraw_state,\n                RedrawState::Queued | RedrawState::WaitingForEstimatedVBlankAndQueued(_)\n            )\n        }) {\n            trace!(\"redrawing output\");\n            let output = output.clone();\n            self.redraw(backend, &output);\n        }\n    }\n\n    pub fn render_pointer<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        output: &Output,\n        push: &mut dyn FnMut(PointerRenderElements<R>),\n    ) {\n        let _span = tracy_client::span!(\"Niri::render_pointer\");\n        let output_scale = output.current_scale();\n        let output_pos = self.global_space.output_geometry(output).unwrap().loc;\n\n        // Check whether we need to draw the tablet cursor or the regular cursor.\n        let pointer_pos = self\n            .tablet_cursor_location\n            .unwrap_or_else(|| self.seat.get_pointer().unwrap().current_location());\n        let pointer_pos = pointer_pos - output_pos.to_f64();\n\n        // Get the render cursor to draw.\n        let cursor_scale = output_scale.integer_scale();\n        let render_cursor = self.cursor_manager.get_render_cursor(cursor_scale);\n\n        let output_scale = Scale::from(output.current_scale().fractional_scale());\n\n        match render_cursor {\n            RenderCursor::Hidden => (),\n            RenderCursor::Surface { surface, hotspot } => {\n                let pointer_pos =\n                    (pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);\n\n                push_elements_from_surface_tree(\n                    renderer,\n                    &surface,\n                    pointer_pos,\n                    output_scale,\n                    1.,\n                    Kind::Cursor,\n                    &mut |elem| push(elem.into()),\n                );\n            }\n            RenderCursor::Named {\n                icon,\n                scale,\n                cursor,\n            } => {\n                let (idx, frame) = cursor.frame(self.start_time.elapsed().as_millis() as u32);\n                let hotspot = XCursor::hotspot(frame).to_logical(scale);\n                let pointer_pos =\n                    (pointer_pos - hotspot.to_f64()).to_physical_precise_round(output_scale);\n\n                let texture = self.cursor_texture_cache.get(icon, scale, &cursor, idx);\n                match MemoryRenderBufferRenderElement::from_buffer(\n                    renderer,\n                    pointer_pos,\n                    &texture,\n                    None,\n                    None,\n                    None,\n                    Kind::Cursor,\n                ) {\n                    Ok(element) => push(element.into()),\n                    Err(err) => {\n                        warn!(\"error importing a cursor texture: {err:?}\");\n                    }\n                }\n            }\n        }\n\n        if let Some(dnd_icon) = self.dnd_icon.as_ref() {\n            let pointer_pos =\n                (pointer_pos + dnd_icon.offset.to_f64()).to_physical_precise_round(output_scale);\n            push_elements_from_surface_tree(\n                renderer,\n                &dnd_icon.surface,\n                pointer_pos,\n                output_scale,\n                1.,\n                Kind::ScanoutCandidate,\n                &mut |elem| push(elem.into()),\n            );\n        }\n    }\n\n    /// Checks if the pointer should be included on a window cast or screenshot.\n    ///\n    /// Returns `(cursor_global_pos, win_pos)` if the pointer should be included, or `None`\n    /// otherwise.\n    pub fn pointer_pos_for_window_cast(\n        &self,\n        mapped: &Mapped,\n    ) -> Option<(Point<f64, Logical>, Point<f64, Logical>)> {\n        // Tablet cursor.\n        if let Some(tablet_pos) = self.tablet_cursor_location {\n            let contents = self.contents_under(tablet_pos);\n            if let Some((w, HitType::Input { win_pos })) = contents.window {\n                if w == mapped.window {\n                    // Tablet tools don't currently expose current focus, and don't currently\n                    // have grabs. When those are implemented, this branch should be adjusted\n                    // to look more similar to the branch below.\n                    return Some((tablet_pos, win_pos));\n                }\n            }\n        }\n        // Regular cursor.\n        else if let Some((w, HitType::Input { win_pos })) = &self.pointer_contents.window {\n            if w == &mapped.window {\n                // Grabs can modify the pointer focus, making it different from\n                // pointer_contents. Notably, gestures like Mod+MMB will remove the pointer\n                // focus, and ClickGrab will keep pointer focus on the clicked window even\n                // while it's moving over a different window.\n                //\n                // So, double-check that current_focus() (after grabs) also matches the pointer\n                // contents.\n                let pointer = self.seat.get_pointer().unwrap();\n\n                // The DnD grab is a bit special because it has its own focus (data device)\n                // while the pointer focus is cleared. That focus is not currently exposed from\n                // Smithay, and showing DnD icons on window screenshots seems useful, so let's\n                // just allow it during DnD grabs.\n                let is_dnd_grab = pointer\n                    .with_grab(|_, grab| State::is_dnd_grab(grab.as_any()))\n                    .unwrap_or(false);\n\n                let current_focus_matches = is_dnd_grab\n                    || pointer\n                        .current_focus()\n                        .map(|focused| self.find_root_shell_surface(&focused))\n                        .is_some_and(|focused| mapped.is_wl_surface(&focused));\n                if current_focus_matches {\n                    // We don't check for pointer visibility because it can only be Visible or\n                    // Hidden, and never Disabled (then it wouldn't have focus). Even when the\n                    // pointer is Hidden, we want to render it, since the user explicitly\n                    // requested show_pointer = true, and otherwise there's no easy way to\n                    // screenshot a window with pointer with hide-when-typing because pressing\n                    // the screenshot bind will hide the pointer.\n                    return Some((pointer.current_location(), *win_pos));\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn refresh_pointer_outputs(&mut self) {\n        if !self.pointer_visibility.is_visible() {\n            return;\n        }\n\n        let _span = tracy_client::span!(\"Niri::refresh_pointer_outputs\");\n\n        // Check whether we need to draw the tablet cursor or the regular cursor.\n        let pointer_pos = self\n            .tablet_cursor_location\n            .unwrap_or_else(|| self.seat.get_pointer().unwrap().current_location());\n\n        match self.cursor_manager.cursor_image() {\n            CursorImageStatus::Surface(ref surface) => {\n                let hotspot = with_states(surface, |states| {\n                    states\n                        .data_map\n                        .get::<CursorImageSurfaceData>()\n                        .unwrap()\n                        .lock()\n                        .unwrap()\n                        .hotspot\n                });\n\n                let surface_pos = pointer_pos.to_i32_round() - hotspot;\n                let bbox = bbox_from_surface_tree(surface, surface_pos);\n\n                let dnd = self\n                    .dnd_icon\n                    .as_ref()\n                    .map(|icon| &icon.surface)\n                    .map(|surface| (surface, bbox_from_surface_tree(surface, surface_pos)));\n\n                // FIXME we basically need to pick the largest scale factor across the overlapping\n                // outputs, this is how it's usually done in clients as well.\n                let mut cursor_scale = 1.;\n                let mut cursor_transform = Transform::Normal;\n                let mut dnd_scale = 1.;\n                let mut dnd_transform = Transform::Normal;\n                for output in self.global_space.outputs() {\n                    let geo = self.global_space.output_geometry(output).unwrap();\n\n                    // Compute pointer surface overlap.\n                    if let Some(mut overlap) = geo.intersection(bbox) {\n                        overlap.loc -= surface_pos;\n                        cursor_scale =\n                            f64::max(cursor_scale, output.current_scale().fractional_scale());\n                        // FIXME: using the largest overlapping or \"primary\" output transform would\n                        // make more sense here.\n                        cursor_transform = output.current_transform();\n                        output_update(output, Some(overlap), surface);\n                    } else {\n                        output_update(output, None, surface);\n                    }\n\n                    // Compute DnD icon surface overlap.\n                    if let Some((surface, bbox)) = dnd {\n                        if let Some(mut overlap) = geo.intersection(bbox) {\n                            overlap.loc -= surface_pos;\n                            dnd_scale =\n                                f64::max(dnd_scale, output.current_scale().fractional_scale());\n                            // FIXME: using the largest overlapping or \"primary\" output transform\n                            // would make more sense here.\n                            dnd_transform = output.current_transform();\n                            output_update(output, Some(overlap), surface);\n                        } else {\n                            output_update(output, None, surface);\n                        }\n                    }\n                }\n\n                with_states(surface, |data| {\n                    send_scale_transform(\n                        surface,\n                        data,\n                        output::Scale::Fractional(cursor_scale),\n                        cursor_transform,\n                    )\n                });\n                if let Some((surface, _)) = dnd {\n                    with_states(surface, |data| {\n                        send_scale_transform(\n                            surface,\n                            data,\n                            output::Scale::Fractional(dnd_scale),\n                            dnd_transform,\n                        );\n                    });\n                }\n            }\n            cursor_image => {\n                // There's no cursor surface, but there might be a DnD icon.\n                let Some(surface) = self.dnd_icon.as_ref().map(|icon| &icon.surface) else {\n                    return;\n                };\n\n                let icon = if let CursorImageStatus::Named(icon) = cursor_image {\n                    *icon\n                } else {\n                    Default::default()\n                };\n\n                let mut dnd_scale = 1.;\n                let mut dnd_transform = Transform::Normal;\n                for output in self.global_space.outputs() {\n                    let geo = self.global_space.output_geometry(output).unwrap();\n\n                    // The default cursor is rendered at the right scale for each output, which\n                    // means that it may have a different hotspot for each output.\n                    let output_scale = output.current_scale().integer_scale();\n                    let cursor = self\n                        .cursor_manager\n                        .get_cursor_with_name(icon, output_scale)\n                        .unwrap_or_else(|| self.cursor_manager.get_default_cursor(output_scale));\n\n                    // For simplicity, we always use frame 0 for this computation. Let's hope the\n                    // hotspot doesn't change between frames.\n                    let hotspot = XCursor::hotspot(&cursor.frames()[0]).to_logical(output_scale);\n\n                    let surface_pos = pointer_pos.to_i32_round() - hotspot;\n                    let bbox = bbox_from_surface_tree(surface, surface_pos);\n\n                    if let Some(mut overlap) = geo.intersection(bbox) {\n                        overlap.loc -= surface_pos;\n                        dnd_scale = f64::max(dnd_scale, output.current_scale().fractional_scale());\n                        // FIXME: using the largest overlapping or \"primary\" output transform would\n                        // make more sense here.\n                        dnd_transform = output.current_transform();\n                        output_update(output, Some(overlap), surface);\n                    } else {\n                        output_update(output, None, surface);\n                    }\n                }\n\n                with_states(surface, |data| {\n                    send_scale_transform(\n                        surface,\n                        data,\n                        output::Scale::Fractional(dnd_scale),\n                        dnd_transform,\n                    );\n                });\n            }\n        }\n    }\n\n    pub fn refresh_layout(&mut self) {\n        let layout_is_active = match &self.keyboard_focus {\n            KeyboardFocus::Layout { .. } => true,\n            KeyboardFocus::LayerShell { .. } => false,\n\n            // Draw layout as active in these cases to reduce unnecessary window animations.\n            // There's no confusion because these are both fullscreen modes.\n            //\n            // FIXME: when going into the screenshot UI from a layer-shell focus, and then back to\n            // layer-shell, the layout will briefly draw as active, despite never having focus.\n            KeyboardFocus::LockScreen { .. } => true,\n            KeyboardFocus::ScreenshotUi => true,\n            KeyboardFocus::ExitConfirmDialog => true,\n            KeyboardFocus::Overview => true,\n            KeyboardFocus::Mru => true,\n        };\n\n        self.layout.refresh(layout_is_active);\n    }\n\n    pub fn refresh_idle_inhibit(&mut self) {\n        let _span = tracy_client::span!(\"Niri::refresh_idle_inhibit\");\n\n        self.idle_inhibiting_surfaces.retain(|s| s.is_alive());\n\n        let is_inhibited = self.is_fdo_idle_inhibited.load(Ordering::SeqCst)\n            || self.idle_inhibiting_surfaces.iter().any(|surface| {\n                with_states(surface, |states| {\n                    surface_primary_scanout_output(surface, states).is_some()\n                })\n            });\n        self.idle_notifier_state.set_is_inhibited(is_inhibited);\n    }\n\n    pub fn refresh_window_states(&mut self) {\n        let _span = tracy_client::span!(\"Niri::refresh_window_states\");\n\n        let config = self.config.borrow();\n        self.layout.with_windows_mut(|mapped, _output| {\n            mapped.update_tiled_state(config.prefer_no_csd);\n        });\n        drop(config);\n    }\n\n    pub fn refresh_window_rules(&mut self) {\n        let _span = tracy_client::span!(\"Niri::refresh_window_rules\");\n\n        let config = self.config.borrow();\n        let window_rules = &config.window_rules;\n\n        let mut windows = vec![];\n        let mut outputs = HashSet::new();\n        self.layout.with_windows_mut(|mapped, output| {\n            if mapped.recompute_window_rules_if_needed(window_rules, self.is_at_startup) {\n                windows.push(mapped.window.clone());\n\n                if let Some(output) = output {\n                    outputs.insert(output.clone());\n                }\n\n                // Since refresh_window_rules() is called after refresh_layout(), we need to update\n                // the tiled state right here, so that it's picked up by the following\n                // send_pending_configure().\n                mapped.update_tiled_state(config.prefer_no_csd);\n            }\n        });\n        drop(config);\n\n        for win in windows {\n            self.layout.update_window(&win, None);\n            win.toplevel()\n                .expect(\"no X11 support\")\n                .send_pending_configure();\n        }\n        for output in outputs {\n            self.queue_redraw(&output);\n        }\n    }\n\n    pub fn advance_animations(&mut self) {\n        let _span = tracy_client::span!(\"Niri::advance_animations\");\n\n        self.layout.advance_animations();\n        self.config_error_notification.advance_animations();\n        self.exit_confirm_dialog.advance_animations();\n        self.screenshot_ui.advance_animations();\n        self.window_mru_ui.advance_animations();\n\n        for state in self.output_state.values_mut() {\n            if let Some(transition) = &mut state.screen_transition {\n                if transition.is_done() {\n                    state.screen_transition = None;\n                }\n            }\n        }\n    }\n\n    pub fn update_render_elements(&mut self, output: Option<&Output>) {\n        self.layout.update_render_elements(output);\n\n        for (out, state) in self.output_state.iter_mut() {\n            if output.is_none_or(|output| out == output) {\n                let scale = Scale::from(out.current_scale().fractional_scale());\n                let transform = out.current_transform();\n\n                if let Some(transition) = &mut state.screen_transition {\n                    transition.update_render_elements(scale, transform);\n                }\n\n                let layer_map = layer_map_for_output(out);\n                for surface in layer_map.layers() {\n                    let Some(mapped) = self.mapped_layer_surfaces.get_mut(surface) else {\n                        continue;\n                    };\n                    let Some(geo) = layer_map.layer_geometry(surface) else {\n                        continue;\n                    };\n\n                    mapped.update_render_elements(geo.size.to_f64());\n                }\n            }\n        }\n    }\n\n    pub fn update_shaders(&mut self) {\n        self.layout.update_shaders();\n\n        for mapped in self.mapped_layer_surfaces.values_mut() {\n            mapped.update_shaders();\n        }\n    }\n\n    pub fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        output: &Output,\n        include_pointer: bool,\n        target: RenderTarget,\n    ) -> Vec<OutputRenderElements<R>> {\n        let mut elements = Vec::new();\n        self.render_inner(renderer, output, include_pointer, target, &mut |elem| {\n            elements.push(elem)\n        });\n        elements\n    }\n\n    pub fn render_inner<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        output: &Output,\n        include_pointer: bool,\n        mut target: RenderTarget,\n        push: &mut dyn FnMut(OutputRenderElements<R>),\n    ) {\n        let _span = tracy_client::span!(\"Niri::render\");\n\n        if target == RenderTarget::Output {\n            if let Some(preview) = self.config.borrow().debug.preview_render {\n                target = match preview {\n                    PreviewRender::Screencast => RenderTarget::Screencast,\n                    PreviewRender::ScreenCapture => RenderTarget::ScreenCapture,\n                };\n            }\n        }\n\n        let output_scale = Scale::from(output.current_scale().fractional_scale());\n\n        let push = if self.debug_draw_opaque_regions {\n            &mut move |elem| {\n                push_opaque_regions(&elem, output_scale, push);\n                push(elem);\n            }\n        } else {\n            push\n        };\n\n        // The pointer goes on the top.\n        if include_pointer && self.pointer_visibility.is_visible() {\n            self.render_pointer(renderer, output, &mut |elem| push(elem.into()));\n        }\n\n        // Next, the screen transition texture.\n        {\n            let state = self.output_state.get(output).unwrap();\n            if let Some(transition) = &state.screen_transition {\n                push(transition.render(target).into());\n            }\n        }\n\n        // Next, the exit confirm dialog.\n        self.exit_confirm_dialog\n            .render(renderer, output, &mut |elem| push(elem.into()));\n\n        // Next, the config error notification too.\n        if let Some(element) = self.config_error_notification.render(renderer, output) {\n            push(element.into());\n        }\n\n        // If the session is locked, draw the lock surface.\n        if self.is_locked() {\n            let state = self.output_state.get(output).unwrap();\n            if let Some(surface) = state.lock_surface.as_ref() {\n                push_elements_from_surface_tree(\n                    renderer,\n                    surface.wl_surface(),\n                    Point::new(0, 0),\n                    output_scale,\n                    1.,\n                    Kind::ScanoutCandidate,\n                    &mut |elem| push(elem.into()),\n                );\n            }\n\n            // Draw the solid color background.\n            push(\n                SolidColorRenderElement::from_buffer(\n                    &state.lock_color_buffer,\n                    (0., 0.),\n                    1.,\n                    Kind::Unspecified,\n                )\n                .into(),\n            );\n\n            return;\n        }\n\n        // Prepare the background elements.\n        let state = self.output_state.get(output).unwrap();\n        let backdrop = SolidColorRenderElement::from_buffer(\n            &state.backdrop_buffer,\n            (0., 0.),\n            1.,\n            Kind::Unspecified,\n        )\n        .into();\n\n        // If the screenshot UI is open, draw it.\n        if self.screenshot_ui.is_open() {\n            self.screenshot_ui\n                .render_output(output, target, &mut |elem| push(elem.into()));\n\n            // Add the backdrop for outputs that were connected while the screenshot UI was open.\n            push(backdrop);\n\n            return;\n        }\n\n        // Draw the hotkey overlay on top.\n        if let Some(element) = self.hotkey_overlay.render(renderer, output) {\n            push(element.into());\n        }\n\n        // Then, the Alt-Tab switcher.\n        self.window_mru_ui\n            .render_output(self, output, renderer, target, &mut |elem| {\n                push(elem.into())\n            });\n\n        // Don't draw the focus ring on the workspaces while interactively moving above those\n        // workspaces, since the interactively-moved window already has a focus ring.\n        let focus_ring = !self.layout.interactive_move_is_moving_above_output(output);\n\n        // Get monitor elements.\n        let mon = self.layout.monitor_for_output(output).unwrap();\n        let zoom = mon.overview_zoom();\n\n        // Get layer-shell elements.\n        let layer_map = layer_map_for_output(output);\n\n        // We use macros instead of closures to avoid borrowing issues (renderer and push() go\n        // into different functions).\n        macro_rules! push_popups_from_layer {\n            ($layer:expr, $backdrop:expr, $push:expr) => {{\n                self.render_layer_popups(renderer, target, &layer_map, $layer, $backdrop, $push);\n            }};\n            ($layer:expr, true) => {{\n                push_popups_from_layer!($layer, true, &mut |elem| push(elem.into()));\n            }};\n            ($layer:expr, $push:expr) => {{\n                push_popups_from_layer!($layer, false, $push);\n            }};\n            ($layer:expr) => {{\n                push_popups_from_layer!($layer, false, &mut |elem| push(elem.into()));\n            }};\n        }\n        macro_rules! push_normal_from_layer {\n            ($layer:expr, $backdrop:expr, $push:expr) => {{\n                self.render_layer_normal(renderer, target, &layer_map, $layer, $backdrop, $push);\n            }};\n            ($layer:expr, true) => {{\n                push_normal_from_layer!($layer, true, &mut |elem| push(elem.into()));\n            }};\n            ($layer:expr, $push:expr) => {{\n                push_normal_from_layer!($layer, false, $push);\n            }};\n            ($layer:expr) => {{\n                push_normal_from_layer!($layer, false, &mut |elem| push(elem.into()));\n            }};\n        }\n\n        // The overlay layer elements go next.\n        push_popups_from_layer!(Layer::Overlay);\n        push_normal_from_layer!(Layer::Overlay);\n\n        // When rendering above the top layer, we put the regular monitor elements first.\n        // Otherwise, we will render all layer-shell pop-ups and the top layer on top.\n        if mon.render_above_top_layer() {\n            self.layout\n                .render_interactive_move_for_output(renderer, output, target, &mut |elem| {\n                    push(elem.into())\n                });\n\n            mon.render_insert_hint_between_workspaces(renderer, &mut |elem| push(elem.into()));\n\n            mon.render_workspaces(renderer, target, focus_ring, &mut |elem| push(elem.into()));\n\n            push_popups_from_layer!(Layer::Top);\n            push_normal_from_layer!(Layer::Top);\n\n            push_popups_from_layer!(Layer::Bottom);\n            push_popups_from_layer!(Layer::Background);\n            push_normal_from_layer!(Layer::Bottom);\n            push_normal_from_layer!(Layer::Background);\n\n            // We don't expect more than one workspace when render_above_top_layer().\n            if let Some((ws, _geo)) = mon.workspaces_with_render_geo().next() {\n                push(ws.render_background().into());\n            }\n        } else {\n            push_popups_from_layer!(Layer::Top);\n            push_normal_from_layer!(Layer::Top);\n\n            self.layout\n                .render_interactive_move_for_output(renderer, output, target, &mut |elem| {\n                    push(elem.into())\n                });\n\n            mon.render_insert_hint_between_workspaces(renderer, &mut |elem| push(elem.into()));\n\n            // Macro instead of closure to avoid borrowing push().\n            macro_rules! process {\n                ($geo:expr) => {{\n                    &mut |elem| {\n                        if let Some(elem) = scale_relocate_crop(elem, output_scale, zoom, $geo) {\n                            push(elem.into());\n                        }\n                    }\n                }};\n            }\n\n            for (_ws, geo) in mon.workspaces_with_render_geo() {\n                push_popups_from_layer!(Layer::Bottom, process!(geo));\n                push_popups_from_layer!(Layer::Background, process!(geo));\n            }\n\n            mon.render_workspaces(renderer, target, focus_ring, &mut |elem| push(elem.into()));\n\n            for (ws, geo) in mon.workspaces_with_render_geo() {\n                push_normal_from_layer!(Layer::Bottom, process!(geo));\n                push_normal_from_layer!(Layer::Background, process!(geo));\n\n                process!(geo)(ws.render_background());\n            }\n        }\n\n        mon.render_workspace_shadows(renderer, &mut |elem| push(elem.into()));\n\n        // Then the backdrop.\n        push_popups_from_layer!(Layer::Background, true);\n        push_normal_from_layer!(Layer::Background, true);\n\n        push(backdrop);\n    }\n\n    fn layers_in_render_order<'a>(\n        &'a self,\n        layer_map: &'a LayerMap,\n        layer: Layer,\n        for_backdrop: bool,\n    ) -> impl Iterator<Item = (&'a MappedLayer, Rectangle<i32, Logical>)> {\n        // LayerMap returns layers in reverse stacking order.\n        layer_map.layers_on(layer).rev().filter_map(move |surface| {\n            let mapped = self.mapped_layer_surfaces.get(surface)?;\n\n            if for_backdrop != mapped.place_within_backdrop() {\n                return None;\n            }\n\n            let geo = layer_map.layer_geometry(surface)?;\n            Some((mapped, geo))\n        })\n    }\n\n    fn render_layer_normal<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        target: RenderTarget,\n        layer_map: &LayerMap,\n        layer: Layer,\n        for_backdrop: bool,\n        push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),\n    ) {\n        for (mapped, geo) in self.layers_in_render_order(layer_map, layer, for_backdrop) {\n            mapped.render_normal(renderer, geo.loc.to_f64(), target, push);\n        }\n    }\n\n    fn render_layer_popups<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        target: RenderTarget,\n        layer_map: &LayerMap,\n        layer: Layer,\n        for_backdrop: bool,\n        push: &mut dyn FnMut(LayerSurfaceRenderElement<R>),\n    ) {\n        for (mapped, geo) in self.layers_in_render_order(layer_map, layer, for_backdrop) {\n            mapped.render_popups(renderer, geo.loc.to_f64(), target, push);\n        }\n    }\n\n    fn redraw(&mut self, backend: &mut Backend, output: &Output) {\n        let _span = tracy_client::span!(\"Niri::redraw\");\n\n        // Verify our invariant.\n        let state = self.output_state.get_mut(output).unwrap();\n        assert!(matches!(\n            state.redraw_state,\n            RedrawState::Queued | RedrawState::WaitingForEstimatedVBlankAndQueued(_)\n        ));\n\n        let target_presentation_time = state.frame_clock.next_presentation_time();\n\n        // Freeze the clock at the target time.\n        self.clock.set_unadjusted(target_presentation_time);\n\n        self.update_render_elements(Some(output));\n\n        let mut res = RenderResult::Skipped;\n        if self.monitors_active {\n            let state = self.output_state.get_mut(output).unwrap();\n            state.unfinished_animations_remain = self.layout.are_animations_ongoing(Some(output));\n            state.unfinished_animations_remain |=\n                self.config_error_notification.are_animations_ongoing();\n            state.unfinished_animations_remain |= self.exit_confirm_dialog.are_animations_ongoing();\n            state.unfinished_animations_remain |= self.screenshot_ui.are_animations_ongoing();\n            state.unfinished_animations_remain |= self.window_mru_ui.are_animations_ongoing();\n            state.unfinished_animations_remain |= state.screen_transition.is_some();\n\n            // Also keep redrawing if the current cursor is animated.\n            state.unfinished_animations_remain |= self\n                .cursor_manager\n                .is_current_cursor_animated(output.current_scale().integer_scale());\n\n            // Also check layer surfaces.\n            if !state.unfinished_animations_remain {\n                state.unfinished_animations_remain |= layer_map_for_output(output)\n                    .layers()\n                    .filter_map(|surface| self.mapped_layer_surfaces.get(surface))\n                    .any(|mapped| mapped.are_animations_ongoing());\n            }\n\n            // Render.\n            res = backend.render(self, output, target_presentation_time);\n        }\n\n        let is_locked = self.is_locked();\n        let state = self.output_state.get_mut(output).unwrap();\n\n        if res == RenderResult::Skipped {\n            // Update the redraw state on failed render.\n            state.redraw_state = if let RedrawState::WaitingForEstimatedVBlank(token)\n            | RedrawState::WaitingForEstimatedVBlankAndQueued(token) =\n                state.redraw_state\n            {\n                RedrawState::WaitingForEstimatedVBlank(token)\n            } else {\n                RedrawState::Idle\n            };\n        }\n\n        // Update the lock render state on successful render, or if monitors are inactive. When\n        // monitors are inactive on a TTY, they have no framebuffer attached, so no sensitive data\n        // from a last render will be visible.\n        if res != RenderResult::Skipped || !self.monitors_active {\n            state.lock_render_state = if is_locked {\n                LockRenderState::Locked\n            } else {\n                LockRenderState::Unlocked\n            };\n        }\n\n        // If we're in process of locking the session, check if the requirements were met.\n        match mem::take(&mut self.lock_state) {\n            LockState::Locking(confirmation) => {\n                if state.lock_render_state == LockRenderState::Unlocked {\n                    // We needed to render a locked frame on this output but failed.\n                    self.unlock();\n                } else {\n                    // Check if all outputs are now locked.\n                    let all_locked = self\n                        .output_state\n                        .values()\n                        .all(|state| state.lock_render_state == LockRenderState::Locked);\n\n                    if all_locked {\n                        // All outputs are locked, report success.\n                        let lock = confirmation.ext_session_lock().clone();\n                        confirmation.lock();\n                        self.lock_state = LockState::Locked(lock);\n                    } else {\n                        // Still waiting for other outputs.\n                        self.lock_state = LockState::Locking(confirmation);\n                    }\n                }\n            }\n            lock_state => self.lock_state = lock_state,\n        }\n\n        self.refresh_on_demand_vrr(backend, output);\n\n        // Send the frame callbacks.\n        //\n        // FIXME: The logic here could be a bit smarter. Currently, during an animation, the\n        // surfaces that are visible for the very last frame (e.g. because the camera is moving\n        // away) will receive frame callbacks, and the surfaces that are invisible but will become\n        // visible next frame will not receive frame callbacks (so they will show stale contents for\n        // one frame). We could advance the animations for the next frame and send frame callbacks\n        // according to the expected new positions.\n        //\n        // However, this should probably be restricted to sending frame callbacks to more surfaces,\n        // to err on the safe side.\n        self.send_frame_callbacks(output);\n        backend.with_primary_renderer(|renderer| {\n            #[cfg(feature = \"xdp-gnome-screencast\")]\n            {\n                // Render and send to PipeWire screencast streams.\n                self.render_for_screen_cast(renderer, output, target_presentation_time);\n\n                // FIXME: when a window is hidden, it should probably still receive frame callbacks\n                // and get rendered for screen cast. This is currently\n                // unimplemented, but happens to work by chance, since output\n                // redrawing is more eager than it should be.\n                self.render_windows_for_screen_cast(renderer, output, target_presentation_time);\n            }\n\n            self.render_for_screencopy_with_damage(renderer, output);\n        });\n    }\n\n    pub fn refresh_on_demand_vrr(&mut self, backend: &mut Backend, output: &Output) {\n        let _span = tracy_client::span!(\"Niri::refresh_on_demand_vrr\");\n\n        let name = output.user_data().get::<OutputName>().unwrap();\n        let on_demand = self\n            .config\n            .borrow()\n            .outputs\n            .find(name)\n            .is_some_and(|output| output.is_vrr_on_demand());\n        if !on_demand {\n            return;\n        }\n\n        let current = self.layout.windows_for_output(output).any(|mapped| {\n            mapped.rules().variable_refresh_rate == Some(true) && {\n                let mut visible = false;\n                mapped.window.with_surfaces(|surface, states| {\n                    if !visible\n                        && surface_primary_scanout_output(surface, states).as_ref() == Some(output)\n                    {\n                        visible = true;\n                    }\n                });\n                visible\n            }\n        });\n\n        backend.set_output_on_demand_vrr(self, output, current);\n    }\n\n    pub fn update_primary_scanout_output(\n        &self,\n        output: &Output,\n        render_element_states: &RenderElementStates,\n    ) {\n        // FIXME: potentially tweak the compare function. The default one currently always prefers a\n        // higher refresh-rate output, which is not always desirable (i.e. with a very small\n        // overlap).\n        //\n        // While we only have cursors and DnD icons crossing output boundaries though, it doesn't\n        // matter all that much.\n        if let CursorImageStatus::Surface(surface) = &self.cursor_manager.cursor_image() {\n            with_surface_tree_downward(\n                surface,\n                (),\n                |_, _, _| TraversalAction::DoChildren(()),\n                |surface, states, _| {\n                    update_surface_primary_scanout_output(\n                        surface,\n                        output,\n                        states,\n                        render_element_states,\n                        default_primary_scanout_output_compare,\n                    );\n                },\n                |_, _, _| true,\n            );\n        }\n\n        if let Some(surface) = self.dnd_icon.as_ref().map(|icon| &icon.surface) {\n            with_surface_tree_downward(\n                surface,\n                (),\n                |_, _, _| TraversalAction::DoChildren(()),\n                |surface, states, _| {\n                    update_surface_primary_scanout_output(\n                        surface,\n                        output,\n                        states,\n                        render_element_states,\n                        default_primary_scanout_output_compare,\n                    );\n                },\n                |_, _, _| true,\n            );\n        }\n\n        // We're only updating the current output's windows and layer surfaces. This should be fine\n        // as in niri they can only be rendered on a single output at a time.\n        //\n        // The reason to do this at all is that it keeps track of whether the surface is visible or\n        // not in a unified way with the pointer surfaces, which makes the logic elsewhere simpler.\n\n        for mapped in self.layout.windows_for_output(output) {\n            let win = &mapped.window;\n            let offscreen_data = mapped.offscreen_data();\n            let offscreen_data = offscreen_data.as_ref();\n\n            win.with_surfaces(|surface, states| {\n                let primary_scanout_output = states\n                    .data_map\n                    .get_or_insert_threadsafe(Mutex::<PrimaryScanoutOutput>::default);\n                let mut primary_scanout_output = primary_scanout_output.lock().unwrap();\n\n                let mut id = Id::from_wayland_resource(surface);\n\n                if let Some(data) = offscreen_data {\n                    // We have offscreen data; it's likely that all surfaces are on it.\n                    if data.states.element_was_presented(id.clone()) {\n                        // If the surface was presented to the offscreen, use the offscreen's id.\n                        id = data.id.clone();\n                    }\n\n                    // If we the surface wasn't presented to the offscreen it can mean:\n                    //\n                    // - The surface was invisible. For example, it's obscured by another surface on\n                    //   the offscreen, or simply isn't mapped.\n                    // - The surface is rendered separately from the offscreen, for example: popups\n                    //   during the window resize animation.\n                    //\n                    // In both of these cases, using the original surface element id and the\n                    // original states is the correct thing to do. We may find the surface in the\n                    // original states (in the second case). Either way, we definitely know it is\n                    // *not* in the offscreen, and we won't miss it.\n                    //\n                    // There's one edge case: if the surface is both in the offscreen and separate,\n                    // and the offscreen itself is invisible, while the separate surface is\n                    // visible. In this case we'll currently mark the surface as invisible. We\n                    // don't really use offscreens like that however, and if we start, it's easy\n                    // enough to fix (need an extra check).\n                }\n\n                primary_scanout_output.update_from_render_element_states(\n                    id,\n                    output,\n                    render_element_states,\n                    |_, _, output, _| output,\n                );\n            });\n        }\n\n        for surface in layer_map_for_output(output).layers() {\n            surface.with_surfaces(|surface, states| {\n                update_surface_primary_scanout_output(\n                    surface,\n                    output,\n                    states,\n                    render_element_states,\n                    // Layer surfaces are shown only on one output at a time.\n                    |_, _, output, _| output,\n                );\n            });\n        }\n\n        if let Some(surface) = &self.output_state[output].lock_surface {\n            with_surface_tree_downward(\n                surface.wl_surface(),\n                (),\n                |_, _, _| TraversalAction::DoChildren(()),\n                |surface, states, _| {\n                    update_surface_primary_scanout_output(\n                        surface,\n                        output,\n                        states,\n                        render_element_states,\n                        default_primary_scanout_output_compare,\n                    );\n                },\n                |_, _, _| true,\n            );\n        }\n    }\n\n    pub fn send_dmabuf_feedbacks(\n        &self,\n        output: &Output,\n        feedback: &SurfaceDmabufFeedback,\n        render_element_states: &RenderElementStates,\n    ) {\n        let _span = tracy_client::span!(\"Niri::send_dmabuf_feedbacks\");\n\n        // We can unconditionally send the current output's feedback to regular and layer-shell\n        // surfaces, as they can only be displayed on a single output at a time. Even if a surface\n        // is currently invisible, this is the DMABUF feedback that it should know about.\n        for mapped in self.layout.windows_for_output(output) {\n            mapped.window.send_dmabuf_feedback(\n                output,\n                |_, _| Some(output.clone()),\n                |surface, _| {\n                    select_dmabuf_feedback(\n                        surface,\n                        render_element_states,\n                        &feedback.render,\n                        &feedback.scanout,\n                    )\n                },\n            );\n        }\n\n        for surface in layer_map_for_output(output).layers() {\n            surface.send_dmabuf_feedback(\n                output,\n                |_, _| Some(output.clone()),\n                |surface, _| {\n                    select_dmabuf_feedback(\n                        surface,\n                        render_element_states,\n                        &feedback.render,\n                        &feedback.scanout,\n                    )\n                },\n            );\n        }\n\n        if let Some(surface) = &self.output_state[output].lock_surface {\n            send_dmabuf_feedback_surface_tree(\n                surface.wl_surface(),\n                output,\n                |_, _| Some(output.clone()),\n                |surface, _| {\n                    select_dmabuf_feedback(\n                        surface,\n                        render_element_states,\n                        &feedback.render,\n                        &feedback.scanout,\n                    )\n                },\n            );\n        }\n\n        if let Some(surface) = self.dnd_icon.as_ref().map(|icon| &icon.surface) {\n            send_dmabuf_feedback_surface_tree(\n                surface,\n                output,\n                surface_primary_scanout_output,\n                |surface, _| {\n                    select_dmabuf_feedback(\n                        surface,\n                        render_element_states,\n                        &feedback.render,\n                        &feedback.scanout,\n                    )\n                },\n            );\n        }\n\n        if let CursorImageStatus::Surface(surface) = &self.cursor_manager.cursor_image() {\n            send_dmabuf_feedback_surface_tree(\n                surface,\n                output,\n                surface_primary_scanout_output,\n                |surface, _| {\n                    select_dmabuf_feedback(\n                        surface,\n                        render_element_states,\n                        &feedback.render,\n                        &feedback.scanout,\n                    )\n                },\n            );\n        }\n    }\n\n    pub fn send_frame_callbacks(&mut self, output: &Output) {\n        let _span = tracy_client::span!(\"Niri::send_frame_callbacks\");\n\n        let state = self.output_state.get(output).unwrap();\n        let sequence = state.frame_callback_sequence;\n\n        let should_send = |surface: &WlSurface, states: &SurfaceData| {\n            // Do the standard primary scanout output check. For pointer surfaces it deduplicates\n            // the frame callbacks across potentially multiple outputs, and for regular windows and\n            // layer-shell surfaces it avoids sending frame callbacks to invisible surfaces.\n            let current_primary_output = surface_primary_scanout_output(surface, states);\n            if current_primary_output.as_ref() != Some(output) {\n                return None;\n            }\n\n            // Next, check the throttling status.\n            let frame_throttling_state = states\n                .data_map\n                .get_or_insert(SurfaceFrameThrottlingState::default);\n            let mut last_sent_at = frame_throttling_state.last_sent_at.borrow_mut();\n\n            let mut send = true;\n\n            // If we already sent a frame callback to this surface this output refresh\n            // cycle, don't send one again to prevent empty-damage commit busy loops.\n            if let Some((last_output, last_sequence)) = &*last_sent_at {\n                if last_output == output && *last_sequence == sequence {\n                    send = false;\n                }\n            }\n\n            if send {\n                *last_sent_at = Some((output.clone(), sequence));\n                Some(output.clone())\n            } else {\n                None\n            }\n        };\n\n        let frame_callback_time = get_monotonic_time();\n\n        for mapped in self.layout.windows_for_output_mut(output) {\n            mapped.send_frame(\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                should_send,\n            );\n        }\n\n        for surface in layer_map_for_output(output).layers() {\n            surface.send_frame(\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                should_send,\n            );\n        }\n\n        if let Some(surface) = &self.output_state[output].lock_surface {\n            send_frames_surface_tree(\n                surface.wl_surface(),\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                should_send,\n            );\n        }\n\n        if let Some(surface) = self.dnd_icon.as_ref().map(|icon| &icon.surface) {\n            send_frames_surface_tree(\n                surface,\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                should_send,\n            );\n        }\n\n        if let CursorImageStatus::Surface(surface) = self.cursor_manager.cursor_image() {\n            send_frames_surface_tree(\n                surface,\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                should_send,\n            );\n        }\n    }\n\n    pub fn send_frame_callbacks_on_fallback_timer(&mut self) {\n        let _span = tracy_client::span!(\"Niri::send_frame_callbacks_on_fallback_timer\");\n\n        // Make up a bogus output; we don't care about it here anyway, just the throttling timer.\n        let output = Output::new(\n            String::new(),\n            PhysicalProperties {\n                size: Size::from((0, 0)),\n                subpixel: Subpixel::Unknown,\n                make: String::new(),\n                model: String::new(),\n                serial_number: String::new(),\n            },\n        );\n        let output = &output;\n\n        let frame_callback_time = get_monotonic_time();\n\n        self.layout.with_windows_mut(|mapped, _| {\n            mapped.send_frame(\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                |_, _| None,\n            );\n        });\n\n        for (output, state) in self.output_state.iter() {\n            for surface in layer_map_for_output(output).layers() {\n                surface.send_frame(\n                    output,\n                    frame_callback_time,\n                    FRAME_CALLBACK_THROTTLE,\n                    |_, _| None,\n                );\n            }\n\n            if let Some(surface) = &state.lock_surface {\n                send_frames_surface_tree(\n                    surface.wl_surface(),\n                    output,\n                    frame_callback_time,\n                    FRAME_CALLBACK_THROTTLE,\n                    |_, _| None,\n                );\n            }\n        }\n\n        if let Some(surface) = &self.dnd_icon.as_ref().map(|icon| &icon.surface) {\n            send_frames_surface_tree(\n                surface,\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                |_, _| None,\n            );\n        }\n\n        if let CursorImageStatus::Surface(surface) = self.cursor_manager.cursor_image() {\n            send_frames_surface_tree(\n                surface,\n                output,\n                frame_callback_time,\n                FRAME_CALLBACK_THROTTLE,\n                |_, _| None,\n            );\n        }\n    }\n\n    pub fn take_presentation_feedbacks(\n        &mut self,\n        output: &Output,\n        render_element_states: &RenderElementStates,\n    ) -> OutputPresentationFeedback {\n        let mut feedback = OutputPresentationFeedback::new(output);\n\n        if let CursorImageStatus::Surface(surface) = &self.cursor_manager.cursor_image() {\n            take_presentation_feedback_surface_tree(\n                surface,\n                &mut feedback,\n                surface_primary_scanout_output,\n                |surface, _| {\n                    surface_presentation_feedback_flags_from_states(surface, render_element_states)\n                },\n            );\n        }\n\n        if let Some(surface) = self.dnd_icon.as_ref().map(|icon| &icon.surface) {\n            take_presentation_feedback_surface_tree(\n                surface,\n                &mut feedback,\n                surface_primary_scanout_output,\n                |surface, _| {\n                    surface_presentation_feedback_flags_from_states(surface, render_element_states)\n                },\n            );\n        }\n\n        for mapped in self.layout.windows_for_output(output) {\n            mapped.window.take_presentation_feedback(\n                &mut feedback,\n                surface_primary_scanout_output,\n                |surface, _| {\n                    surface_presentation_feedback_flags_from_states(surface, render_element_states)\n                },\n            )\n        }\n\n        for surface in layer_map_for_output(output).layers() {\n            surface.take_presentation_feedback(\n                &mut feedback,\n                surface_primary_scanout_output,\n                |surface, _| {\n                    surface_presentation_feedback_flags_from_states(surface, render_element_states)\n                },\n            );\n        }\n\n        if let Some(surface) = &self.output_state[output].lock_surface {\n            take_presentation_feedback_surface_tree(\n                surface.wl_surface(),\n                &mut feedback,\n                surface_primary_scanout_output,\n                |surface, _| {\n                    surface_presentation_feedback_flags_from_states(surface, render_element_states)\n                },\n            );\n        }\n\n        feedback\n    }\n\n    pub fn render_for_screencopy_with_damage(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        output: &Output,\n    ) {\n        let _span = tracy_client::span!(\"Niri::render_for_screencopy_with_damage\");\n\n        let mut screencopy_state = mem::take(&mut self.screencopy_state);\n        let elements = OnceCell::new();\n\n        screencopy_state.with_queues_mut(|queue| {\n            let (damage_tracker, screencopy) = queue.split();\n            if let Some(screencopy) = screencopy {\n                if screencopy.output() == output {\n                    let elements = elements.get_or_init(|| {\n                        self.render(renderer, output, true, RenderTarget::ScreenCapture)\n                    });\n                    // FIXME: skip elements if not including pointers\n                    let render_result = Self::render_for_screencopy_internal(\n                        renderer,\n                        output,\n                        elements,\n                        true,\n                        damage_tracker,\n                        screencopy,\n                    );\n                    match render_result {\n                        Ok((sync, damages)) => {\n                            if let Some(damages) = damages {\n                                // Convert from Physical coordinates back to Buffer coordinates.\n                                let transform = output.current_transform();\n                                let physical_size =\n                                    transform.transform_size(screencopy.buffer_size());\n                                let damages = damages.iter().map(|dmg| {\n                                    dmg.to_logical(1).to_buffer(\n                                        1,\n                                        transform.invert(),\n                                        &physical_size.to_logical(1),\n                                    )\n                                });\n\n                                screencopy.damage(damages);\n                                queue.pop().submit_after_sync(false, sync, &self.event_loop);\n                            } else {\n                                trace!(\"no damage found, waiting till next redraw\");\n                            }\n                        }\n                        Err(err) => {\n                            // Recreate damage tracker to report full damage next check.\n                            *damage_tracker =\n                                OutputDamageTracker::new((0, 0), 1.0, Transform::Normal);\n                            queue.pop();\n                            warn!(\"error rendering for screencopy: {err:?}\");\n                        }\n                    }\n                };\n            }\n        });\n\n        self.screencopy_state = screencopy_state;\n    }\n\n    pub fn render_for_screencopy_without_damage(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        manager: &ZwlrScreencopyManagerV1,\n        screencopy: Screencopy,\n    ) -> anyhow::Result<()> {\n        let _span = tracy_client::span!(\"Niri::render_for_screencopy\");\n\n        let output = screencopy.output();\n        ensure!(\n            self.output_state.contains_key(output),\n            \"screencopy output missing\"\n        );\n\n        self.update_render_elements(Some(output));\n\n        let elements = self.render(\n            renderer,\n            output,\n            screencopy.overlay_cursor(),\n            RenderTarget::ScreenCapture,\n        );\n        let Some(damage_tracker) = self.screencopy_state.damage_tracker(manager) else {\n            error!(\"screencopy queue must not be deleted as long as frames exist\");\n            bail!(\"screencopy queue missing\");\n        };\n\n        let render_result = Self::render_for_screencopy_internal(\n            renderer,\n            output,\n            &elements,\n            false,\n            damage_tracker,\n            &screencopy,\n        );\n\n        let res = render_result\n            .map(|(sync, _damage)| screencopy.submit_after_sync(false, sync, &self.event_loop));\n\n        if res.is_err() {\n            // Recreate damage tracker to report full damage next check.\n            *damage_tracker = OutputDamageTracker::new((0, 0), 1.0, Transform::Normal);\n        }\n\n        res\n    }\n\n    #[allow(clippy::type_complexity)]\n    fn render_for_screencopy_internal<'a>(\n        renderer: &mut GlesRenderer,\n        output: &Output,\n        elements: &[OutputRenderElements<GlesRenderer>],\n        with_damage: bool,\n        damage_tracker: &'a mut OutputDamageTracker,\n        screencopy: &Screencopy,\n    ) -> anyhow::Result<(Option<SyncPoint>, Option<&'a Vec<Rectangle<i32, Physical>>>)> {\n        let OutputModeSource::Static {\n            size: last_size,\n            scale: last_scale,\n            transform: last_transform,\n        } = damage_tracker.mode().clone()\n        else {\n            unreachable!(\"damage tracker must have static mode\");\n        };\n\n        let size = screencopy.buffer_size();\n        let scale: Scale<f64> = output.current_scale().fractional_scale().into();\n        let transform = output.current_transform();\n\n        if size != last_size || scale != last_scale || transform != last_transform {\n            *damage_tracker = OutputDamageTracker::new(size, scale, transform);\n        }\n\n        let region_loc = screencopy.region_loc();\n        let elements = elements\n            .iter()\n            .map(|element| {\n                RelocateRenderElement::from_element(\n                    element,\n                    region_loc.upscale(-1),\n                    Relocate::Relative,\n                )\n            })\n            .collect::<Vec<_>>();\n\n        // Just checked damage tracker has static mode\n        let damages = damage_tracker.damage_output(1, &elements).unwrap().0;\n        if with_damage && damages.is_none() {\n            return Ok((None, None));\n        }\n\n        let elements = elements.iter().rev();\n\n        let sync = match screencopy.buffer() {\n            ScreencopyBuffer::Dmabuf(dmabuf) => {\n                let sync =\n                    render_to_dmabuf(renderer, dmabuf.clone(), size, scale, transform, elements)\n                        .context(\"error rendering to screencopy dmabuf\")?;\n                Some(sync)\n            }\n            ScreencopyBuffer::Shm(wl_buffer) => {\n                render_to_shm(renderer, wl_buffer, size, scale, transform, elements)\n                    .context(\"error rendering to screencopy shm buffer\")?;\n                None\n            }\n        };\n\n        Ok((sync, damages))\n    }\n\n    #[cfg(not(feature = \"xdp-gnome-screencast\"))]\n    pub fn stop_casts_for_target(&mut self, _target: CastTarget) {}\n\n    #[cfg(not(feature = \"xdp-gnome-screencast\"))]\n    pub fn stop_cast(&mut self, _session_id: crate::utils::CastSessionId) {}\n\n    pub fn debug_toggle_damage(&mut self) {\n        self.debug_draw_damage = !self.debug_draw_damage;\n\n        if self.debug_draw_damage {\n            for (output, state) in &mut self.output_state {\n                state.debug_damage_tracker = OutputDamageTracker::from_output(output);\n            }\n        }\n\n        self.queue_redraw_all();\n    }\n\n    pub fn capture_screenshots<'a>(\n        &'a self,\n        renderer: &'a mut GlesRenderer,\n    ) -> impl Iterator<Item = (Output, [OutputScreenshot; 3])> + 'a {\n        self.global_space.outputs().cloned().filter_map(|output| {\n            let size = output.current_mode().unwrap().size;\n            let transform = output.current_transform();\n            let size = transform.transform_size(size);\n\n            let scale = Scale::from(output.current_scale().fractional_scale());\n            let targets = [\n                RenderTarget::Output,\n                RenderTarget::Screencast,\n                RenderTarget::ScreenCapture,\n            ];\n            let screenshot = targets.map(|target| {\n                let elements = self.render::<GlesRenderer>(renderer, &output, false, target);\n                let elements = elements.iter().rev();\n\n                let res = render_to_texture(\n                    renderer,\n                    size,\n                    scale,\n                    Transform::Normal,\n                    Fourcc::Abgr8888,\n                    elements,\n                );\n                if let Err(err) = &res {\n                    warn!(\"error rendering output {}: {err:?}\", output.name());\n                }\n                let res_output = res.ok();\n\n                let mut pointer = Vec::new();\n\n                // We check the pointer visibility for Disabled (and not .is_visible()) in order to\n                // show the pointer even when it's hidden through cursor {} options. The user can\n                // then toggle it in the screenshot UI as needed.\n                if self.pointer_visibility != PointerVisibility::Disabled {\n                    self.render_pointer(renderer, &output, &mut |elem| pointer.push(elem));\n                }\n\n                let res_pointer = if pointer.is_empty() {\n                    None\n                } else {\n                    let res = render_to_encompassing_texture(\n                        renderer,\n                        scale,\n                        Transform::Normal,\n                        Fourcc::Abgr8888,\n                        &pointer,\n                    );\n                    if let Err(err) = &res {\n                        warn!(\"error rendering pointer for {}: {err:?}\", output.name());\n                    }\n                    res.ok()\n                };\n\n                res_output.map(|(texture, _)| {\n                    OutputScreenshot::from_textures(\n                        renderer,\n                        scale,\n                        texture,\n                        res_pointer.map(|(texture, _, geo)| (texture, geo)),\n                    )\n                })\n            });\n\n            if screenshot.iter().any(|res| res.is_none()) {\n                return None;\n            }\n\n            let screenshot = screenshot.map(|res| res.unwrap());\n            Some((output, screenshot))\n        })\n    }\n\n    pub fn screenshot(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        output: &Output,\n        write_to_disk: bool,\n        include_pointer: bool,\n        path: Option<String>,\n    ) -> anyhow::Result<()> {\n        let _span = tracy_client::span!(\"Niri::screenshot\");\n\n        self.update_render_elements(Some(output));\n\n        let size = output.current_mode().unwrap().size;\n        let transform = output.current_transform();\n        let size = transform.transform_size(size);\n\n        let scale = Scale::from(output.current_scale().fractional_scale());\n        let elements = self.render::<GlesRenderer>(\n            renderer,\n            output,\n            include_pointer,\n            RenderTarget::ScreenCapture,\n        );\n        let elements = elements.iter().rev();\n        let pixels = render_to_vec(\n            renderer,\n            size,\n            scale,\n            Transform::Normal,\n            Fourcc::Abgr8888,\n            elements,\n        )?;\n\n        self.save_screenshot(size, pixels, write_to_disk, path)\n            .context(\"error saving screenshot\")\n    }\n\n    pub fn screenshot_window(\n        &self,\n        renderer: &mut GlesRenderer,\n        output: &Output,\n        mapped: &Mapped,\n        write_to_disk: bool,\n        show_pointer: bool,\n        path: Option<String>,\n    ) -> anyhow::Result<()> {\n        let _span = tracy_client::span!(\"Niri::screenshot_window\");\n\n        let scale = Scale::from(output.current_scale().fractional_scale());\n        let alpha =\n            if mapped.sizing_mode().is_fullscreen() || mapped.is_ignoring_opacity_window_rule() {\n                1.\n            } else {\n                mapped.rules().opacity.unwrap_or(1.).clamp(0., 1.)\n            };\n\n        let mut elements: Vec<WindowScreenshotRenderElement<GlesRenderer>> = Vec::new();\n\n        // Add pointer if requested and it's over this window.\n        if show_pointer {\n            if let Some((_, win_pos)) = self.pointer_pos_for_window_cast(mapped) {\n                // Pointer elements are at output-local physical coords.\n                // Relocate by -win_pos to make them window-relative.\n                let pos = win_pos.to_physical_precise_round(scale).upscale(-1);\n                self.render_pointer(renderer, output, &mut |elem| {\n                    let elem = RelocateRenderElement::from_element(elem, pos, Relocate::Relative);\n                    elements.push(elem.into());\n                });\n            }\n        }\n        let pointer_count = elements.len();\n\n        mapped.render(\n            renderer,\n            mapped.window.geometry().loc.to_f64(),\n            scale,\n            alpha,\n            RenderTarget::ScreenCapture,\n            &mut |elem| elements.push(elem.into()),\n        );\n\n        // The pointer is not included in encompassing_geo because we don't want it to expand the\n        // screenshot size.\n        let geo = encompassing_geo(scale, elements.iter().skip(pointer_count));\n        let elements = elements.iter().rev().map(|elem| {\n            RelocateRenderElement::from_element(elem, geo.loc.upscale(-1), Relocate::Relative)\n        });\n        let pixels = render_to_vec(\n            renderer,\n            geo.size,\n            scale,\n            Transform::Normal,\n            Fourcc::Abgr8888,\n            elements,\n        )?;\n\n        self.save_screenshot(geo.size, pixels, write_to_disk, path)\n            .context(\"error saving screenshot\")\n    }\n\n    pub fn save_screenshot(\n        &self,\n        size: Size<i32, Physical>,\n        pixels: Vec<u8>,\n        write_to_disk: bool,\n        path_arg: Option<String>,\n    ) -> anyhow::Result<()> {\n        let path = write_to_disk\n            .then(|| {\n                // When given an explicit path, don't try to strftime it or create parents.\n                path_arg.map(|p| (PathBuf::from(p), false)).or_else(|| {\n                    match make_screenshot_path(&self.config.borrow()) {\n                        Ok(path) => path.map(|p| (p, true)),\n                        Err(err) => {\n                            warn!(\"error making screenshot path: {err:?}\");\n                            None\n                        }\n                    }\n                })\n            })\n            .flatten();\n\n        // Prepare to set the encoded image as our clipboard selection. This must be done from the\n        // main thread.\n        let (tx, rx) = calloop::channel::sync_channel::<Arc<[u8]>>(1);\n        self.event_loop\n            .insert_source(rx, move |event, _, state| match event {\n                calloop::channel::Event::Msg(buf) => {\n                    set_data_device_selection(\n                        &state.niri.display_handle,\n                        &state.niri.seat,\n                        vec![String::from(\"image/png\")],\n                        buf.clone(),\n                    );\n                }\n                calloop::channel::Event::Closed => (),\n            })\n            .unwrap();\n\n        // Prepare to send screenshot completion event back to main thread.\n        let (event_tx, event_rx) = calloop::channel::sync_channel::<Option<String>>(1);\n        self.event_loop\n            .insert_source(event_rx, move |event, _, state| match event {\n                calloop::channel::Event::Msg(path) => {\n                    state.ipc_screenshot_taken(path);\n                }\n                calloop::channel::Event::Closed => (),\n            })\n            .unwrap();\n\n        // Encode and save the image in a thread as it's slow.\n        thread::spawn(move || {\n            let mut buf = vec![];\n\n            let w = std::io::Cursor::new(&mut buf);\n            if let Err(err) = write_png_rgba8(w, size.w as u32, size.h as u32, &pixels) {\n                warn!(\"error encoding screenshot image: {err:?}\");\n                return;\n            }\n\n            let buf: Arc<[u8]> = Arc::from(buf.into_boxed_slice());\n            let _ = tx.send(buf.clone());\n\n            let mut image_path = None;\n\n            if let Some((path, create_parent)) = path {\n                debug!(\"saving screenshot to {path:?}\");\n\n                if create_parent {\n                    if let Some(parent) = path.parent() {\n                        // Relative paths with one component, i.e. \"test.png\", have Some(\"\") parent.\n                        if !parent.as_os_str().is_empty() {\n                            if let Err(err) = std::fs::create_dir_all(parent) {\n                                if err.kind() != std::io::ErrorKind::AlreadyExists {\n                                    warn!(\"error creating screenshot directory: {err:?}\");\n                                }\n                            }\n                        }\n                    }\n                }\n\n                match std::fs::write(&path, buf) {\n                    Ok(()) => image_path = Some(path),\n                    Err(err) => {\n                        warn!(\"error saving screenshot image: {err:?}\");\n                    }\n                }\n            } else {\n                debug!(\"not saving screenshot to disk\");\n            }\n\n            #[cfg(feature = \"dbus\")]\n            if let Err(err) = crate::utils::show_screenshot_notification(image_path.as_deref()) {\n                warn!(\"error showing screenshot notification: {err:?}\");\n            }\n\n            // Send screenshot completion event.\n            let path_string = image_path\n                .as_ref()\n                .and_then(|p| p.to_str())\n                .map(|s| s.to_owned());\n            let _ = event_tx.send(path_string);\n        });\n\n        Ok(())\n    }\n\n    #[cfg(feature = \"dbus\")]\n    pub fn screenshot_all_outputs(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        include_pointer: bool,\n        on_done: impl FnOnce(PathBuf) + Send + 'static,\n    ) -> anyhow::Result<()> {\n        let _span = tracy_client::span!(\"Niri::screenshot_all_outputs\");\n\n        self.update_render_elements(None);\n\n        let outputs: Vec<_> = self.global_space.outputs().cloned().collect();\n\n        // FIXME: support multiple outputs, needs fixing multi-scale handling and cropping.\n        anyhow::ensure!(outputs.len() == 1);\n\n        let output = outputs.into_iter().next().unwrap();\n        let geom = self.global_space.output_geometry(&output).unwrap();\n\n        let output_scale = output.current_scale().integer_scale();\n        let geom = geom.to_physical(output_scale);\n\n        let size = geom.size;\n        let transform = output.current_transform();\n        let size = transform.transform_size(size);\n\n        let elements = self.render::<GlesRenderer>(\n            renderer,\n            &output,\n            include_pointer,\n            RenderTarget::ScreenCapture,\n        );\n        let elements = elements.iter().rev();\n        let pixels = render_to_vec(\n            renderer,\n            size,\n            Scale::from(f64::from(output_scale)),\n            Transform::Normal,\n            Fourcc::Abgr8888,\n            elements,\n        )?;\n\n        let path = make_screenshot_path(&self.config.borrow())\n            .ok()\n            .flatten()\n            .unwrap_or_else(|| {\n                let mut path = env::temp_dir();\n                path.push(\"screenshot.png\");\n                path\n            });\n        debug!(\"saving screenshot to {path:?}\");\n\n        thread::spawn(move || {\n            let file = match std::fs::File::create(&path) {\n                Ok(file) => file,\n                Err(err) => {\n                    warn!(\"error creating file: {err:?}\");\n                    return;\n                }\n            };\n\n            let w = std::io::BufWriter::new(file);\n            if let Err(err) = write_png_rgba8(w, size.w as u32, size.h as u32, &pixels) {\n                warn!(\"error encoding screenshot image: {err:?}\");\n                return;\n            }\n\n            on_done(path);\n        });\n\n        Ok(())\n    }\n\n    pub fn is_locked(&self) -> bool {\n        match self.lock_state {\n            LockState::Unlocked | LockState::WaitingForSurfaces { .. } => false,\n            LockState::Locking(_) | LockState::Locked(_) => true,\n        }\n    }\n\n    pub fn lock(&mut self, confirmation: SessionLocker) {\n        // Check if another client is in the process of locking.\n        if matches!(\n            self.lock_state,\n            LockState::WaitingForSurfaces { .. } | LockState::Locking(_)\n        ) {\n            info!(\"refusing lock as another client is currently locking\");\n            return;\n        }\n\n        // Check if we're already locked with an active client.\n        if let LockState::Locked(lock) = &self.lock_state {\n            if lock.is_alive() {\n                info!(\"refusing lock as already locked with an active client\");\n                return;\n            }\n\n            // If the client had died, continue with the new lock.\n            info!(\"locking session (replacing existing dead lock)\");\n\n            // Since the session was already locked, we know that the outputs are blanked, and\n            // can lock right away.\n            let lock = confirmation.ext_session_lock().clone();\n            confirmation.lock();\n            self.lock_state = LockState::Locked(lock);\n\n            return;\n        }\n\n        info!(\"locking session\");\n\n        if self.output_state.is_empty() {\n            // There are no outputs, lock the session right away.\n            self.screenshot_ui.close();\n            self.cursor_manager\n                .set_cursor_image(CursorImageStatus::default_named());\n\n            let lock = confirmation.ext_session_lock().clone();\n            confirmation.lock();\n            self.lock_state = LockState::Locked(lock);\n        } else {\n            // There are outputs which we need to redraw before locking. But before we do that,\n            // let's wait for the lock surfaces.\n            //\n            // Give them a second; swaylock can take its time to paint a big enough image.\n            let timer = Timer::from_duration(Duration::from_millis(1000));\n            let deadline_token = self\n                .event_loop\n                .insert_source(timer, |_, _, state| {\n                    trace!(\"lock deadline expired, continuing\");\n                    state.niri.continue_to_locking();\n                    TimeoutAction::Drop\n                })\n                .unwrap();\n\n            self.lock_state = LockState::WaitingForSurfaces {\n                confirmation,\n                deadline_token,\n            };\n        }\n    }\n\n    pub fn maybe_continue_to_locking(&mut self) {\n        if !matches!(self.lock_state, LockState::WaitingForSurfaces { .. }) {\n            // Not waiting.\n            return;\n        }\n\n        // Check if there are any outputs whose lock surfaces had not had a commit yet.\n        for state in self.output_state.values() {\n            let Some(surface) = &state.lock_surface else {\n                // Surface not created yet.\n                return;\n            };\n\n            if !is_mapped(surface.wl_surface()) {\n                return;\n            }\n        }\n\n        // All good.\n        trace!(\"lock surfaces are ready, continuing\");\n        self.continue_to_locking();\n    }\n\n    fn continue_to_locking(&mut self) {\n        match mem::take(&mut self.lock_state) {\n            LockState::WaitingForSurfaces {\n                confirmation,\n                deadline_token,\n            } => {\n                self.event_loop.remove(deadline_token);\n\n                self.screenshot_ui.close();\n                self.cursor_manager\n                    .set_cursor_image(CursorImageStatus::default_named());\n                self.cancel_mru();\n\n                if self.output_state.is_empty() {\n                    // There are no outputs, lock the session right away.\n                    let lock = confirmation.ext_session_lock().clone();\n                    confirmation.lock();\n                    self.lock_state = LockState::Locked(lock);\n                } else {\n                    // There are outputs which we need to redraw before locking.\n                    self.lock_state = LockState::Locking(confirmation);\n                    self.queue_redraw_all();\n                }\n            }\n            other => {\n                error!(\"continue_to_locking() called with wrong lock state: {other:?}\",);\n                self.lock_state = other;\n            }\n        }\n    }\n\n    pub fn unlock(&mut self) {\n        info!(\"unlocking session\");\n\n        let prev = mem::take(&mut self.lock_state);\n        if let LockState::WaitingForSurfaces { deadline_token, .. } = prev {\n            self.event_loop.remove(deadline_token);\n        }\n\n        for output_state in self.output_state.values_mut() {\n            output_state.lock_surface = None;\n        }\n        self.queue_redraw_all();\n    }\n\n    #[cfg(feature = \"dbus\")]\n    fn update_locked_hint(&mut self) {\n        use std::sync::LazyLock;\n\n        if !self.is_session_instance {\n            return;\n        }\n\n        static XDG_SESSION_ID: LazyLock<Option<String>> = LazyLock::new(|| {\n            let id = std::env::var(\"XDG_SESSION_ID\").ok();\n            if id.is_none() {\n                warn!(\n                    \"env var 'XDG_SESSION_ID' is unset or invalid; logind LockedHint won't be set\"\n                );\n            }\n            id\n        });\n\n        let Some(session_id) = &*XDG_SESSION_ID else {\n            return;\n        };\n\n        fn call(session_id: &str, locked: bool) -> anyhow::Result<()> {\n            let conn = zbus::blocking::Connection::system()\n                .context(\"error connecting to the system bus\")?;\n\n            let message = conn\n                .call_method(\n                    Some(\"org.freedesktop.login1\"),\n                    \"/org/freedesktop/login1\",\n                    Some(\"org.freedesktop.login1.Manager\"),\n                    \"GetSession\",\n                    &(session_id),\n                )\n                .context(\"failed to call GetSession\")?;\n\n            let message_body = message.body();\n            let session_path: zbus::zvariant::ObjectPath = message_body\n                .deserialize()\n                .context(\"failed to deserialize GetSession reply\")?;\n\n            conn.call_method(\n                Some(\"org.freedesktop.login1\"),\n                session_path,\n                Some(\"org.freedesktop.login1.Session\"),\n                \"SetLockedHint\",\n                &(locked),\n            )\n            .context(\"failed to call SetLockedHint\")?;\n\n            Ok(())\n        }\n\n        // Consider only the fully locked state here. When using the locked hint with sleep\n        // inhibitor tools, we want to allow sleep only after the screens are fully cleared with\n        // the lock screen, which corresponds to the Locked state.\n        let locked = matches!(self.lock_state, LockState::Locked(_));\n\n        if self.locked_hint.is_some_and(|h| h == locked) {\n            return;\n        }\n\n        self.locked_hint = Some(locked);\n\n        let res = thread::Builder::new()\n            .name(\"Logind LockedHint Updater\".to_owned())\n            .spawn(move || {\n                let _span = tracy_client::span!(\"LockedHint\");\n\n                if let Err(err) = call(session_id, locked) {\n                    warn!(\"failed to set logind LockedHint: {err:?}\");\n                }\n            });\n\n        if let Err(err) = res {\n            warn!(\"error spawning a thread to set logind LockedHint: {err:?}\");\n        }\n    }\n\n    pub fn new_lock_surface(&mut self, surface: LockSurface, output: &Output) {\n        let lock = match &self.lock_state {\n            LockState::Unlocked => {\n                error!(\"tried to add a lock surface on an unlocked session\");\n                return;\n            }\n            LockState::WaitingForSurfaces { confirmation, .. } => confirmation.ext_session_lock(),\n            LockState::Locking(confirmation) => confirmation.ext_session_lock(),\n            LockState::Locked(lock) => lock,\n        };\n\n        if lock.client() != surface.wl_surface().client() {\n            debug!(\"ignoring lock surface from an unrelated client\");\n            return;\n        }\n\n        let Some(output_state) = self.output_state.get_mut(output) else {\n            error!(\"missing output state\");\n            return;\n        };\n\n        output_state.lock_surface = Some(surface);\n    }\n\n    /// Activates the pointer constraint if necessary according to the current pointer contents.\n    ///\n    /// Make sure the pointer location and contents are up to date before calling this.\n    pub fn maybe_activate_pointer_constraint(&self) {\n        let Some((surface, surface_loc)) = &self.pointer_contents.surface else {\n            return;\n        };\n\n        let pointer = self.seat.get_pointer().unwrap();\n        if Some(surface) != pointer.current_focus().as_ref() {\n            return;\n        }\n\n        with_pointer_constraint(surface, &pointer, |constraint| {\n            let Some(constraint) = constraint else { return };\n\n            if constraint.is_active() {\n                return;\n            }\n\n            // Constraint does not apply if not within region.\n            if let Some(region) = constraint.region() {\n                let pointer_pos = pointer.current_location();\n                let pos_within_surface = pointer_pos - *surface_loc;\n                if !region.contains(pos_within_surface.to_i32_round()) {\n                    return;\n                }\n            }\n\n            constraint.activate();\n        });\n    }\n\n    pub fn focus_layer_surface_if_on_demand(&mut self, surface: Option<LayerSurface>) {\n        if let Some(surface) = surface {\n            if surface.cached_state().keyboard_interactivity\n                == wlr_layer::KeyboardInteractivity::OnDemand\n            {\n                if self.layer_shell_on_demand_focus.as_ref() != Some(&surface) {\n                    self.layer_shell_on_demand_focus = Some(surface);\n\n                    // FIXME: granular.\n                    self.queue_redraw_all();\n                }\n\n                return;\n            }\n        }\n\n        // Something else got clicked, clear on-demand layer-shell focus.\n        if self.layer_shell_on_demand_focus.is_some() {\n            self.layer_shell_on_demand_focus = None;\n\n            // FIXME: granular.\n            self.queue_redraw_all();\n        }\n    }\n\n    /// Tries to find and return the root shell surface for a given surface.\n    ///\n    /// I.e. for popups, this function will try to find the parent toplevel or layer surface. For\n    /// regular subsurfaces, it will find the root surface.\n    pub fn find_root_shell_surface(&self, surface: &WlSurface) -> WlSurface {\n        let Some(root) = self.root_surface.get(surface) else {\n            return surface.clone();\n        };\n\n        if let Some(popup) = self.popups.find_popup(root) {\n            return find_popup_root_surface(&popup).unwrap_or_else(|_| root.clone());\n        }\n\n        root.clone()\n    }\n\n    #[cfg(feature = \"dbus\")]\n    pub fn on_ipc_outputs_changed(&self) {\n        let _span = tracy_client::span!(\"Niri::on_ipc_outputs_changed\");\n\n        let Some(dbus) = &self.dbus else { return };\n        let Some(conn_display_config) = dbus.conn_display_config.clone() else {\n            return;\n        };\n\n        let res = thread::Builder::new()\n            .name(\"DisplayConfig MonitorsChanged Emitter\".to_owned())\n            .spawn(move || {\n                use crate::dbus::mutter_display_config::DisplayConfig;\n                let _span = tracy_client::span!(\"MonitorsChanged\");\n                let iface = match conn_display_config\n                    .object_server()\n                    .interface::<_, DisplayConfig>(\"/org/gnome/Mutter/DisplayConfig\")\n                {\n                    Ok(iface) => iface,\n                    Err(err) => {\n                        warn!(\"error getting DisplayConfig interface: {err:?}\");\n                        return;\n                    }\n                };\n\n                async_io::block_on(async move {\n                    if let Err(err) = DisplayConfig::monitors_changed(iface.signal_emitter()).await\n                    {\n                        warn!(\"error emitting MonitorsChanged: {err:?}\");\n                    }\n                });\n            });\n\n        if let Err(err) = res {\n            warn!(\"error spawning a thread to send MonitorsChanged: {err:?}\");\n        }\n    }\n\n    pub fn handle_focus_follows_mouse(&mut self, new_focus: &PointContents) {\n        let Some(ffm) = self.config.borrow().input.focus_follows_mouse else {\n            return;\n        };\n\n        let pointer = &self.seat.get_pointer().unwrap();\n        if pointer.is_grabbed() {\n            return;\n        }\n\n        if self.window_mru_ui.is_open() {\n            return;\n        }\n\n        // Recompute the current pointer focus because we don't update it during animations.\n        let current_focus = self.contents_under(pointer.current_location());\n\n        if let Some(output) = &new_focus.output {\n            if current_focus.output.as_ref() != Some(output) {\n                self.layout.focus_output(output);\n            }\n        }\n\n        if let Some(window) = &new_focus.window {\n            if !self.layout.is_overview_open() && current_focus.window.as_ref() != Some(window) {\n                let (window, hit) = window;\n\n                // Don't trigger focus-follows-mouse over the tab indicator.\n                if matches!(\n                    hit,\n                    HitType::Activate {\n                        is_tab_indicator: true\n                    }\n                ) {\n                    return;\n                }\n\n                if !self.layout.should_trigger_focus_follows_mouse_on(window) {\n                    return;\n                }\n\n                if let Some(threshold) = ffm.max_scroll_amount {\n                    if self.layout.scroll_amount_to_activate(window) > threshold.0 {\n                        return;\n                    }\n                }\n\n                self.layout.activate_window_without_raising(window);\n                self.layer_shell_on_demand_focus = None;\n            }\n        }\n\n        if let Some(layer) = &new_focus.layer {\n            if current_focus.layer.as_ref() != Some(layer) {\n                self.layer_shell_on_demand_focus = Some(layer.clone());\n            }\n        }\n    }\n\n    pub fn do_screen_transition(&mut self, renderer: &mut GlesRenderer, delay_ms: Option<u16>) {\n        let _span = tracy_client::span!(\"Niri::do_screen_transition\");\n\n        self.update_render_elements(None);\n\n        let textures: Vec<_> = self\n            .output_state\n            .keys()\n            .cloned()\n            .filter_map(|output| {\n                let size = output.current_mode().unwrap().size;\n                let transform = output.current_transform();\n\n                let scale = Scale::from(output.current_scale().fractional_scale());\n                let targets = [\n                    RenderTarget::Output,\n                    RenderTarget::Screencast,\n                    RenderTarget::ScreenCapture,\n                ];\n                let textures = targets.map(|target| {\n                    let elements = self.render::<GlesRenderer>(renderer, &output, false, target);\n                    let elements = elements.iter().rev();\n\n                    let res = render_to_texture(\n                        renderer,\n                        size,\n                        scale,\n                        transform,\n                        Fourcc::Abgr8888,\n                        elements,\n                    );\n\n                    if let Err(err) = &res {\n                        warn!(\"error rendering output {}: {err:?}\", output.name());\n                    }\n\n                    res\n                });\n\n                if textures.iter().any(|res| res.is_err()) {\n                    return None;\n                }\n\n                let textures = textures.map(|res| {\n                    let texture = res.unwrap().0;\n                    TextureBuffer::from_texture(\n                        renderer,\n                        texture,\n                        scale,\n                        transform,\n                        Vec::new(), // We want windows below to get frame callbacks.\n                    )\n                });\n\n                Some((output, textures))\n            })\n            .collect();\n\n        let delay = delay_ms.map_or(screen_transition::DELAY, |d| {\n            Duration::from_millis(u64::from(d))\n        });\n\n        for (output, from_texture) in textures {\n            let state = self.output_state.get_mut(&output).unwrap();\n            state.screen_transition = Some(ScreenTransition::new(\n                from_texture,\n                delay,\n                self.clock.clone(),\n            ));\n        }\n\n        // We don't actually need to queue a redraw because the point is to freeze the screen for a\n        // bit, and even if the delay was zero, we're drawing the same contents anyway.\n    }\n\n    pub fn recompute_window_rules(&mut self) {\n        let _span = tracy_client::span!(\"Niri::recompute_window_rules\");\n\n        let changed = {\n            let window_rules = &self.config.borrow().window_rules;\n\n            for unmapped in self.unmapped_windows.values_mut() {\n                let new_rules = ResolvedWindowRules::compute(\n                    window_rules,\n                    WindowRef::Unmapped(unmapped),\n                    self.is_at_startup,\n                );\n                if let InitialConfigureState::Configured { rules, .. } = &mut unmapped.state {\n                    *rules = new_rules;\n                }\n            }\n\n            let mut windows = vec![];\n            self.layout.with_windows_mut(|mapped, _| {\n                if mapped.recompute_window_rules(window_rules, self.is_at_startup) {\n                    windows.push(mapped.window.clone());\n                }\n            });\n            let changed = !windows.is_empty();\n            for win in windows {\n                self.layout.update_window(&win, None);\n            }\n            changed\n        };\n\n        if changed {\n            // FIXME: granular.\n            self.queue_redraw_all();\n        }\n    }\n\n    pub fn recompute_layer_rules(&mut self) {\n        let _span = tracy_client::span!(\"Niri::recompute_layer_rules\");\n\n        let mut changed = false;\n        {\n            let config = self.config.borrow();\n            let rules = &config.layer_rules;\n\n            for mapped in self.mapped_layer_surfaces.values_mut() {\n                if mapped.recompute_layer_rules(rules, self.is_at_startup) {\n                    changed = true;\n                    mapped.update_config(&config);\n                }\n            }\n        }\n\n        if changed {\n            // FIXME: granular.\n            self.queue_redraw_all();\n        }\n    }\n\n    pub fn reset_pointer_inactivity_timer(&mut self) {\n        if self.pointer_inactivity_timer_got_reset {\n            return;\n        }\n\n        let _span = tracy_client::span!(\"Niri::reset_pointer_inactivity_timer\");\n\n        if let Some(token) = self.pointer_inactivity_timer.take() {\n            self.event_loop.remove(token);\n        }\n\n        let Some(timeout_ms) = self.config.borrow().cursor.hide_after_inactive_ms else {\n            return;\n        };\n\n        let duration = Duration::from_millis(timeout_ms as u64);\n        let timer = Timer::from_duration(duration);\n        let token = self\n            .event_loop\n            .insert_source(timer, move |_, _, state| {\n                state.niri.pointer_inactivity_timer = None;\n\n                // If the pointer is already invisible, don't reset it back to Hidden causing one\n                // frame of hover.\n                if state.niri.pointer_visibility.is_visible() {\n                    state.niri.pointer_visibility = PointerVisibility::Hidden;\n                    state.niri.queue_redraw_all();\n                }\n\n                TimeoutAction::Drop\n            })\n            .unwrap();\n        self.pointer_inactivity_timer = Some(token);\n\n        self.pointer_inactivity_timer_got_reset = true;\n    }\n\n    pub fn notify_activity(&mut self) {\n        if self.notified_activity_this_iteration {\n            return;\n        }\n\n        let _span = tracy_client::span!(\"Niri::notify_activity\");\n\n        self.idle_notifier_state.notify_activity(&self.seat);\n\n        self.notified_activity_this_iteration = true;\n    }\n\n    pub fn close_mru(&mut self, close_request: MruCloseRequest) -> Option<Window> {\n        if !self.window_mru_ui.is_open() {\n            return None;\n        }\n        self.queue_redraw_all();\n\n        let id = self.window_mru_ui.close(close_request)?;\n        self.find_window_by_id(id)\n    }\n\n    pub fn cancel_mru(&mut self) {\n        self.close_mru(MruCloseRequest::Cancel);\n    }\n\n    /// Apply a pending MRU commit immediately.\n    ///\n    /// Called for example on keyboard events that reach the active window, which immediately adds\n    /// it to the MRU.\n    pub fn mru_apply_keyboard_commit(&mut self) {\n        let Some(pending) = self.pending_mru_commit.take() else {\n            return;\n        };\n        self.event_loop.remove(pending.token);\n\n        if let Some(window) = self\n            .layout\n            .workspaces_mut()\n            .flat_map(|ws| ws.windows_mut())\n            .find(|w| w.id() == pending.id)\n        {\n            window.set_focus_timestamp(pending.stamp);\n        }\n    }\n\n    pub fn queue_redraw_mru_output(&mut self) {\n        if let Some(output) = self.window_mru_ui.output().cloned() {\n            self.queue_redraw(&output);\n        }\n    }\n}\n\npub struct NewClient {\n    pub client: UnixStream,\n    pub restricted: bool,\n    pub credentials_unknown: bool,\n}\n\npub struct ClientState {\n    pub compositor_state: CompositorClientState,\n    pub can_view_decoration_globals: bool,\n    pub primary_selection_disabled: bool,\n    /// Whether this client is denied from the restricted protocols such as security-context.\n    pub restricted: bool,\n    /// We cannot retrieve this client's socket credentials.\n    pub credentials_unknown: bool,\n}\n\nimpl ClientData for ClientState {\n    fn initialized(&self, _client_id: ClientId) {}\n    fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}\n}\n\nfn scale_relocate_crop<E: Element>(\n    elem: E,\n    output_scale: Scale<f64>,\n    zoom: f64,\n    ws_geo: Rectangle<f64, Logical>,\n) -> Option<CropRenderElement<RelocateRenderElement<RescaleRenderElement<E>>>> {\n    let ws_geo = ws_geo.to_physical_precise_round(output_scale);\n    let elem = RescaleRenderElement::from_element(elem, Point::from((0, 0)), zoom);\n    let elem = RelocateRenderElement::from_element(elem, ws_geo.loc, Relocate::Relative);\n    CropRenderElement::from_element(elem, output_scale, ws_geo)\n}\n\nniri_render_elements! {\n    PointerRenderElements<R> => {\n        Wayland = WaylandSurfaceRenderElement<R>,\n        NamedPointer = MemoryRenderBufferRenderElement<R>,\n    }\n}\n\nniri_render_elements! {\n    WindowScreenshotRenderElement<R> => {\n        Layout = LayoutElementRenderElement<R>,\n        Pointer = RelocateRenderElement<PointerRenderElements<R>>,\n    }\n}\n\nniri_render_elements! {\n    OutputRenderElements<R> => {\n        Monitor = MonitorRenderElement<R>,\n        RescaledTile = RescaleRenderElement<TileRenderElement<R>>,\n        LayerSurface = LayerSurfaceRenderElement<R>,\n        RelocatedLayerSurface = CropRenderElement<RelocateRenderElement<RescaleRenderElement<\n            LayerSurfaceRenderElement<R>\n        >>>,\n        RelocatedColor = CropRenderElement<RelocateRenderElement<RescaleRenderElement<\n            SolidColorRenderElement\n        >>>,\n        Pointer = PointerRenderElements<R>,\n        Wayland = WaylandSurfaceRenderElement<R>,\n        SolidColor = SolidColorRenderElement,\n        ScreenshotUi = ScreenshotUiRenderElement,\n        WindowMruUi = WindowMruUiRenderElement<R>,\n        ExitConfirmDialog = ExitConfirmDialogRenderElement,\n        Texture = PrimaryGpuTextureRenderElement,\n        // Used for the CPU-rendered panels.\n        RelocatedMemoryBuffer = RelocateRenderElement<MemoryRenderBufferRenderElement<R>>,\n    }\n}\n"
  },
  {
    "path": "src/protocols/ext_workspace.rs",
    "content": "//! ext-workspace protocol implementation.\n//!\n//! This is how we map the protocol concepts to the niri concepts:\n//!\n//! - Workspace groups are outputs.\n//! - Workspace coordinates: X = 0, Y = workspace index. They need to be two-dimensional because 1D\n//!   coordinates are defined to be a plain list without a geometric interpretation, while we do\n//!   order workspaces in a vertical line.\n//! - Workspace id: name for named workspaces, unset for unnamed. Because ids in this protocol are\n//!   expected to be stable across sessions.\n//! - Workspace name: name for named workspaces, index for unnamed.\n\nuse std::collections::hash_map::Entry;\nuse std::collections::HashMap;\nuse std::mem;\n\nuse arrayvec::ArrayVec;\nuse ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1;\nuse ext_workspace_handle_v1::ExtWorkspaceHandleV1;\nuse ext_workspace_manager_v1::ExtWorkspaceManagerV1;\nuse smithay::output::{Output, WeakOutput};\nuse smithay::reexports::wayland_protocols::ext::workspace::v1::server::{\n    ext_workspace_group_handle_v1, ext_workspace_handle_v1, ext_workspace_manager_v1,\n};\nuse smithay::reexports::wayland_server::protocol::wl_output::WlOutput;\nuse smithay::reexports::wayland_server::{\n    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,\n};\nuse wayland_backend::server::ClientId;\n\nuse crate::layout::monitor::Monitor;\nuse crate::layout::workspace::{Workspace, WorkspaceId};\nuse crate::niri::State;\nuse crate::window::Mapped;\n\nconst VERSION: u32 = 1;\n\npub trait ExtWorkspaceHandler {\n    fn ext_workspace_manager_state(&mut self) -> &mut ExtWorkspaceManagerState;\n    fn activate_workspace(&mut self, id: WorkspaceId);\n    fn assign_workspace(&mut self, ws_id: WorkspaceId, output: Output);\n}\n\nenum Action {\n    Assign(WorkspaceId, WeakOutput),\n    Activate(WorkspaceId),\n}\n\nimpl Action {\n    fn order(&self) -> u8 {\n        // First assign everything (move across outputs), then activate.\n        match self {\n            Action::Assign(_, _) => 0,\n            Action::Activate(_) => 1,\n        }\n    }\n}\n\npub struct ExtWorkspaceManagerState {\n    display: DisplayHandle,\n    instances: HashMap<ExtWorkspaceManagerV1, Vec<Action>>,\n    workspace_groups: HashMap<Output, ExtWorkspaceGroupData>,\n    workspaces: HashMap<WorkspaceId, ExtWorkspaceData>,\n}\n\nstruct ExtWorkspaceGroupData {\n    instances: Vec<ExtWorkspaceGroupHandleV1>,\n}\n\nstruct ExtWorkspaceData {\n    // id cannot change once set.\n    id: Option<String>,\n    name: String,\n    coordinates: ArrayVec<u32, 2>,\n    state: ext_workspace_handle_v1::State,\n    instances: Vec<ExtWorkspaceHandleV1>,\n    output: Option<Output>,\n}\n\npub struct ExtWorkspaceGlobalData {\n    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,\n}\n\npub fn refresh(state: &mut State) {\n    let _span = tracy_client::span!(\"ext_workspace::refresh\");\n\n    let protocol_state = &mut state.niri.ext_workspace_state;\n\n    let mut changed = false;\n\n    // Remove workspaces that no longer exist (sending workspace_leave to workspace groups).\n    let mut seen_workspaces = HashMap::new();\n    for (mon, _, ws) in state.niri.layout.workspaces() {\n        let output = mon.map(|mon| mon.output());\n        seen_workspaces.insert(ws.id(), output);\n    }\n\n    protocol_state.workspaces.retain(|id, workspace| {\n        if seen_workspaces.contains_key(id) {\n            return true;\n        }\n\n        remove_workspace_instances(&protocol_state.workspace_groups, workspace);\n        changed = true;\n        false\n    });\n\n    // Remove workspace groups for outputs that no longer exist.\n    protocol_state.workspace_groups.retain(|output, data| {\n        if state.niri.sorted_outputs.contains(output) {\n            return true;\n        }\n\n        for group in &data.instances {\n            // Send workspace_leave for all workspaces in this group with matching manager.\n            let manager: &ExtWorkspaceManagerV1 = group.data().unwrap();\n            for ws in protocol_state.workspaces.values() {\n                if ws.output.as_ref() == Some(output) {\n                    for workspace in &ws.instances {\n                        if workspace.data() == Some(manager) {\n                            group.workspace_leave(workspace);\n                        }\n                    }\n                }\n            }\n\n            group.removed();\n        }\n\n        changed = true;\n        false\n    });\n\n    // Update existing workspaces and create new ones.\n    for (mon, ws_idx, ws) in state.niri.layout.workspaces() {\n        changed |= refresh_workspace(protocol_state, mon, ws_idx, ws);\n    }\n\n    // Update workspace groups and create new ones, sending workspace_enter events as needed.\n    for output in &state.niri.sorted_outputs {\n        changed |= refresh_workspace_group(protocol_state, output);\n    }\n\n    if changed {\n        for manager in protocol_state.instances.keys() {\n            manager.done();\n        }\n    }\n}\n\npub fn on_output_bound(state: &mut State, output: &Output, wl_output: &WlOutput) {\n    let Some(client) = wl_output.client() else {\n        return;\n    };\n\n    let mut sent = false;\n\n    let protocol_state = &mut state.niri.ext_workspace_state;\n    if let Some(data) = protocol_state.workspace_groups.get_mut(output) {\n        for group in &mut data.instances {\n            if group.client().as_ref() != Some(&client) {\n                continue;\n            }\n\n            group.output_enter(wl_output);\n            sent = true;\n        }\n    }\n\n    if !sent {\n        return;\n    }\n\n    for manager in protocol_state.instances.keys() {\n        if manager.client().as_ref() == Some(&client) {\n            manager.done();\n        }\n    }\n}\n\nfn refresh_workspace_group(protocol_state: &mut ExtWorkspaceManagerState, output: &Output) -> bool {\n    if protocol_state.workspace_groups.contains_key(output) {\n        // Existing workspace group. Nothing can actually change since our workspace groups are tied\n        // to an output.\n        return false;\n    }\n\n    // New workspace group, start tracking it.\n    let mut data = ExtWorkspaceGroupData {\n        instances: Vec::new(),\n    };\n\n    // Create workspace group handle for each manager instance.\n    for manager in protocol_state.instances.keys() {\n        if let Some(client) = manager.client() {\n            data.add_instance::<State>(&protocol_state.display, &client, manager, output);\n        }\n    }\n\n    // Send workspace_enter for all existing workspaces on this output.\n    for group in &data.instances {\n        let manager: &ExtWorkspaceManagerV1 = group.data().unwrap();\n        for (_, ws) in protocol_state.workspaces.iter() {\n            if ws.output.as_ref() != Some(output) {\n                continue;\n            }\n            for workspace in &ws.instances {\n                if workspace.data() == Some(manager) {\n                    group.workspace_enter(workspace);\n                }\n            }\n        }\n    }\n\n    protocol_state.workspace_groups.insert(output.clone(), data);\n    true\n}\n\nfn send_workspace_enter_leave(\n    workspace_groups: &HashMap<Output, ExtWorkspaceGroupData>,\n    data: &ExtWorkspaceData,\n    enter: bool,\n) {\n    if let Some(output) = &data.output {\n        if let Some(group_data) = workspace_groups.get(output) {\n            for group in &group_data.instances {\n                let manager: &ExtWorkspaceManagerV1 = group.data().unwrap();\n                for workspace in &data.instances {\n                    if workspace.data() == Some(manager) {\n                        if enter {\n                            group.workspace_enter(workspace);\n                        } else {\n                            group.workspace_leave(workspace);\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n\nfn remove_workspace_instances(\n    workspace_groups: &HashMap<Output, ExtWorkspaceGroupData>,\n    data: &ExtWorkspaceData,\n) {\n    send_workspace_enter_leave(workspace_groups, data, false);\n\n    for workspace in &data.instances {\n        workspace.removed();\n    }\n}\n\nfn build_name(ws: &Workspace<Mapped>, ws_idx: usize) -> String {\n    ws.name().cloned().unwrap_or_else(|| {\n        // Add 1 since this is a human-readable name, and our action indexing is 1-based.\n        (ws_idx + 1).to_string()\n    })\n}\n\nfn refresh_workspace(\n    protocol_state: &mut ExtWorkspaceManagerState,\n    mon: Option<&Monitor<Mapped>>,\n    ws_idx: usize,\n    ws: &Workspace<Mapped>,\n) -> bool {\n    let mut state = ext_workspace_handle_v1::State::empty();\n    if mon.is_some_and(|mon| mon.active_workspace_idx() == ws_idx) {\n        state |= ext_workspace_handle_v1::State::Active;\n    }\n    if ws.is_urgent() {\n        state |= ext_workspace_handle_v1::State::Urgent;\n    }\n\n    let output = mon.map(|mon| mon.output());\n\n    match protocol_state.workspaces.entry(ws.id()) {\n        Entry::Occupied(entry) => {\n            // Existing workspace, check if anything changed.\n            let data = entry.into_mut();\n\n            let mut id_set = false;\n            let mut recreate = false;\n            let id = ws.name();\n            if data.id.as_ref() != id {\n                if data.id.is_some() {\n                    recreate = true;\n                } else {\n                    id_set = true;\n                }\n                data.id = id.cloned();\n            }\n\n            let mut coordinates_changed = false;\n            if data.coordinates[1] != ws_idx as u32 {\n                data.coordinates[1] = ws_idx as u32;\n                coordinates_changed = true;\n            }\n\n            let mut state_changed = false;\n            if data.state != state {\n                data.state = state;\n                state_changed = true;\n            }\n\n            // Recreate means name got changed or unset (meaning data.name is back to ws_idx).\n            let check = recreate\n                || if data.id.is_some() {\n                    // True means workspace got named, going from ws_idx to name.\n                    id_set\n                } else {\n                    // The workspace is unnamed, check if ws_idx changed.\n                    coordinates_changed\n                };\n            let mut name_changed = false;\n            if check {\n                let new_name = build_name(ws, ws_idx);\n                // This will likely be true, except if the workspace got named its index.\n                if data.name != new_name {\n                    data.name = new_name;\n                    name_changed = true;\n                }\n            }\n\n            let mut output_changed = false;\n            if data.output.as_ref() != output {\n                send_workspace_enter_leave(&protocol_state.workspace_groups, data, false);\n                data.output = output.cloned();\n                output_changed = true;\n            }\n\n            if recreate {\n                remove_workspace_instances(&protocol_state.workspace_groups, data);\n                data.instances.clear();\n\n                for manager in protocol_state.instances.keys() {\n                    if let Some(client) = manager.client() {\n                        data.add_instance::<State>(&protocol_state.display, &client, manager);\n                    }\n                }\n\n                send_workspace_enter_leave(&protocol_state.workspace_groups, data, true);\n                return true;\n            }\n\n            if output_changed {\n                // Send workspace_enter to the new output's group. If the group doesn't exist yet\n                // (new groups are created after refreshing workspaces), then workspace_enter() will\n                // be sent when the group is created.\n                send_workspace_enter_leave(&protocol_state.workspace_groups, data, true);\n            }\n\n            let something_changed = id_set || name_changed || coordinates_changed || state_changed;\n            if something_changed {\n                for instance in &data.instances {\n                    if id_set {\n                        instance.id(data.id.clone().unwrap());\n                    }\n                    if name_changed {\n                        instance.name(data.name.clone());\n                    }\n                    if coordinates_changed {\n                        instance.coordinates(\n                            data.coordinates\n                                .iter()\n                                .flat_map(|x| x.to_ne_bytes())\n                                .collect(),\n                        );\n                    }\n                    if state_changed {\n                        instance.state(data.state);\n                    }\n                }\n            }\n\n            output_changed || something_changed\n        }\n        Entry::Vacant(entry) => {\n            // New workspace, start tracking it.\n            let mut data = ExtWorkspaceData {\n                id: ws.name().cloned(),\n                name: build_name(ws, ws_idx),\n                coordinates: ArrayVec::from([0, ws_idx as u32]),\n                state,\n                instances: Vec::new(),\n                output: output.cloned(),\n            };\n\n            for manager in protocol_state.instances.keys() {\n                if let Some(client) = manager.client() {\n                    data.add_instance::<State>(&protocol_state.display, &client, manager);\n                }\n            }\n\n            send_workspace_enter_leave(&protocol_state.workspace_groups, &data, true);\n            entry.insert(data);\n            true\n        }\n    }\n}\n\nimpl ExtWorkspaceGroupData {\n    fn add_instance<D>(\n        &mut self,\n        handle: &DisplayHandle,\n        client: &Client,\n        manager: &ExtWorkspaceManagerV1,\n        output: &Output,\n    ) -> &ExtWorkspaceGroupHandleV1\n    where\n        D: Dispatch<ExtWorkspaceGroupHandleV1, ExtWorkspaceManagerV1>,\n        D: 'static,\n    {\n        let group = client\n            .create_resource::<ExtWorkspaceGroupHandleV1, _, D>(\n                handle,\n                manager.version(),\n                manager.clone(),\n            )\n            .unwrap();\n        manager.workspace_group(&group);\n\n        group.capabilities(ext_workspace_group_handle_v1::GroupCapabilities::empty());\n\n        for wl_output in output.client_outputs(client) {\n            group.output_enter(&wl_output);\n        }\n\n        self.instances.push(group);\n        self.instances.last().unwrap()\n    }\n}\n\nimpl ExtWorkspaceData {\n    fn add_instance<D>(\n        &mut self,\n        handle: &DisplayHandle,\n        client: &Client,\n        manager: &ExtWorkspaceManagerV1,\n    ) -> &ExtWorkspaceHandleV1\n    where\n        D: Dispatch<ExtWorkspaceHandleV1, ExtWorkspaceManagerV1>,\n        D: 'static,\n    {\n        let workspace = client\n            .create_resource::<ExtWorkspaceHandleV1, _, D>(\n                handle,\n                manager.version(),\n                manager.clone(),\n            )\n            .unwrap();\n        manager.workspace(&workspace);\n\n        if let Some(id) = self.id.clone() {\n            workspace.id(id);\n        }\n\n        workspace.name(self.name.clone());\n        workspace.coordinates(\n            self.coordinates\n                .iter()\n                .flat_map(|x| x.to_ne_bytes())\n                .collect(),\n        );\n        workspace.state(self.state);\n        workspace.capabilities(\n            ext_workspace_handle_v1::WorkspaceCapabilities::Activate\n                | ext_workspace_handle_v1::WorkspaceCapabilities::Assign,\n        );\n\n        self.instances.push(workspace);\n        self.instances.last().unwrap()\n    }\n}\n\nimpl ExtWorkspaceManagerState {\n    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self\n    where\n        D: GlobalDispatch<ExtWorkspaceManagerV1, ExtWorkspaceGlobalData>,\n        D: Dispatch<ExtWorkspaceManagerV1, ()>,\n        D: 'static,\n        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,\n    {\n        let global_data = ExtWorkspaceGlobalData {\n            filter: Box::new(filter),\n        };\n        display.create_global::<D, ExtWorkspaceManagerV1, _>(VERSION, global_data);\n        Self {\n            display: display.clone(),\n            instances: HashMap::new(),\n            workspace_groups: HashMap::new(),\n            workspaces: HashMap::new(),\n        }\n    }\n}\n\nimpl<D> GlobalDispatch<ExtWorkspaceManagerV1, ExtWorkspaceGlobalData, D>\n    for ExtWorkspaceManagerState\nwhere\n    D: GlobalDispatch<ExtWorkspaceManagerV1, ExtWorkspaceGlobalData>,\n    D: Dispatch<ExtWorkspaceManagerV1, ()>,\n    D: Dispatch<ExtWorkspaceHandleV1, ExtWorkspaceManagerV1>,\n    D: ExtWorkspaceHandler,\n{\n    fn bind(\n        state: &mut D,\n        handle: &DisplayHandle,\n        client: &Client,\n        resource: New<ExtWorkspaceManagerV1>,\n        _global_data: &ExtWorkspaceGlobalData,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        let manager = data_init.init(resource, ());\n\n        let state = state.ext_workspace_manager_state();\n\n        // Send existing workspaces to the new client.\n        let mut new_workspaces: HashMap<_, Vec<_>> = HashMap::new();\n        for data in state.workspaces.values_mut() {\n            let output = data.output.clone();\n            let workspace = data.add_instance::<State>(handle, client, &manager);\n\n            if let Some(output) = output {\n                new_workspaces.entry(output).or_default().push(workspace);\n            }\n        }\n\n        // Create workspace groups for all outputs.\n        for (output, group_data) in &mut state.workspace_groups {\n            let group = group_data.add_instance::<State>(handle, client, &manager, output);\n\n            for workspace in new_workspaces.get(output).into_iter().flatten() {\n                group.workspace_enter(workspace);\n            }\n        }\n\n        manager.done();\n        state.instances.insert(manager, Vec::new());\n    }\n\n    fn can_view(client: Client, global_data: &ExtWorkspaceGlobalData) -> bool {\n        (global_data.filter)(&client)\n    }\n}\n\nimpl<D> Dispatch<ExtWorkspaceManagerV1, (), D> for ExtWorkspaceManagerState\nwhere\n    D: Dispatch<ExtWorkspaceManagerV1, ()>,\n    D: ExtWorkspaceHandler,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        resource: &ExtWorkspaceManagerV1,\n        request: <ExtWorkspaceManagerV1 as Resource>::Request,\n        _data: &(),\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            ext_workspace_manager_v1::Request::Commit => {\n                let protocol_state = state.ext_workspace_manager_state();\n                let actions = protocol_state.instances.get_mut(resource).unwrap();\n                let mut actions = mem::take(actions);\n\n                actions.sort_by_key(Action::order);\n\n                for action in actions {\n                    match action {\n                        Action::Assign(ws_id, output) => {\n                            if let Some(output) = output.upgrade() {\n                                state.assign_workspace(ws_id, output);\n                            }\n                        }\n                        Action::Activate(id) => state.activate_workspace(id),\n                    }\n                }\n            }\n            ext_workspace_manager_v1::Request::Stop => {\n                resource.finished();\n\n                let state = state.ext_workspace_manager_state();\n                state.instances.retain(|x, _| x != resource);\n\n                for data in state.workspace_groups.values_mut() {\n                    data.instances\n                        .retain(|instance| instance.data() != Some(resource));\n                }\n\n                for data in state.workspaces.values_mut() {\n                    data.instances\n                        .retain(|instance| instance.data() != Some(resource));\n                }\n            }\n            _ => unreachable!(),\n        }\n    }\n\n    fn destroyed(state: &mut D, _client: ClientId, resource: &ExtWorkspaceManagerV1, _data: &()) {\n        let state = state.ext_workspace_manager_state();\n        state.instances.retain(|x, _| x != resource);\n    }\n}\n\nimpl<D> Dispatch<ExtWorkspaceHandleV1, ExtWorkspaceManagerV1, D> for ExtWorkspaceManagerState\nwhere\n    D: Dispatch<ExtWorkspaceHandleV1, ExtWorkspaceManagerV1>,\n    D: ExtWorkspaceHandler,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        resource: &ExtWorkspaceHandleV1,\n        request: <ExtWorkspaceHandleV1 as Resource>::Request,\n        data: &ExtWorkspaceManagerV1,\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        let protocol_state = state.ext_workspace_manager_state();\n\n        let Some((workspace, _)) = protocol_state\n            .workspaces\n            .iter()\n            .find(|(_, data)| data.instances.contains(resource))\n        else {\n            return;\n        };\n        let workspace = *workspace;\n\n        match request {\n            ext_workspace_handle_v1::Request::Activate => {\n                let actions = protocol_state.instances.get_mut(data).unwrap();\n                actions.push(Action::Activate(workspace));\n            }\n            ext_workspace_handle_v1::Request::Deactivate => (),\n            ext_workspace_handle_v1::Request::Assign { workspace_group } => {\n                if let Some(output) = protocol_state\n                    .workspace_groups\n                    .iter()\n                    .find(|(_, data)| data.instances.contains(&workspace_group))\n                    .map(|(output, _)| output.clone())\n                {\n                    let actions = protocol_state.instances.get_mut(data).unwrap();\n                    actions.push(Action::Assign(workspace, output.downgrade()));\n                }\n            }\n            ext_workspace_handle_v1::Request::Remove => (),\n            ext_workspace_handle_v1::Request::Destroy => (),\n            _ => unreachable!(),\n        }\n    }\n\n    fn destroyed(\n        state: &mut D,\n        _client: ClientId,\n        resource: &ExtWorkspaceHandleV1,\n        _data: &ExtWorkspaceManagerV1,\n    ) {\n        let state = state.ext_workspace_manager_state();\n        for data in state.workspaces.values_mut() {\n            data.instances.retain(|instance| instance != resource);\n        }\n    }\n}\n\nimpl<D> Dispatch<ExtWorkspaceGroupHandleV1, ExtWorkspaceManagerV1, D> for ExtWorkspaceManagerState\nwhere\n    D: Dispatch<ExtWorkspaceGroupHandleV1, ExtWorkspaceManagerV1>,\n    D: ExtWorkspaceHandler,\n{\n    fn request(\n        _state: &mut D,\n        _client: &Client,\n        _resource: &ExtWorkspaceGroupHandleV1,\n        request: <ExtWorkspaceGroupHandleV1 as Resource>::Request,\n        _data: &ExtWorkspaceManagerV1,\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            ext_workspace_group_handle_v1::Request::CreateWorkspace { .. } => (),\n            ext_workspace_group_handle_v1::Request::Destroy => (),\n            _ => unreachable!(),\n        }\n    }\n\n    fn destroyed(\n        state: &mut D,\n        _client: ClientId,\n        resource: &ExtWorkspaceGroupHandleV1,\n        _data: &ExtWorkspaceManagerV1,\n    ) {\n        let state = state.ext_workspace_manager_state();\n        for data in state.workspace_groups.values_mut() {\n            data.instances.retain(|instance| instance != resource);\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! delegate_ext_workspace {\n    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {\n        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_manager_v1::ExtWorkspaceManagerV1: $crate::protocols::ext_workspace::ExtWorkspaceGlobalData\n        ] => $crate::protocols::ext_workspace::ExtWorkspaceManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_manager_v1::ExtWorkspaceManagerV1: ()\n        ] => $crate::protocols::ext_workspace::ExtWorkspaceManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_handle_v1::ExtWorkspaceHandleV1: smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_manager_v1::ExtWorkspaceManagerV1\n        ] => $crate::protocols::ext_workspace::ExtWorkspaceManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1: smithay::reexports::wayland_protocols::ext::workspace::v1::server::ext_workspace_manager_v1::ExtWorkspaceManagerV1\n        ] => $crate::protocols::ext_workspace::ExtWorkspaceManagerState);\n    };\n}\n"
  },
  {
    "path": "src/protocols/foreign_toplevel.rs",
    "content": "use std::collections::hash_map::Entry;\nuse std::collections::HashMap;\n\nuse arrayvec::ArrayVec;\nuse smithay::output::Output;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;\nuse smithay::reexports::wayland_protocols_wlr;\nuse smithay::reexports::wayland_server::backend::ClientId;\nuse smithay::reexports::wayland_server::protocol::wl_output::WlOutput;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::reexports::wayland_server::{\n    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,\n};\nuse smithay::wayland::shell::xdg::{\n    ToplevelState, ToplevelStateSet, XdgToplevelSurfaceRoleAttributes,\n};\nuse wayland_protocols_wlr::foreign_toplevel::v1::server::{\n    zwlr_foreign_toplevel_handle_v1, zwlr_foreign_toplevel_manager_v1,\n};\nuse zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1;\nuse zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1;\n\nuse crate::niri::State;\nuse crate::utils::with_toplevel_role_and_current;\n\nconst VERSION: u32 = 3;\n\npub struct ForeignToplevelManagerState {\n    display: DisplayHandle,\n    instances: Vec<ZwlrForeignToplevelManagerV1>,\n    toplevels: HashMap<WlSurface, ToplevelData>,\n}\n\npub trait ForeignToplevelHandler {\n    fn foreign_toplevel_manager_state(&mut self) -> &mut ForeignToplevelManagerState;\n    fn activate(&mut self, wl_surface: WlSurface);\n    fn close(&mut self, wl_surface: WlSurface);\n    fn set_fullscreen(&mut self, wl_surface: WlSurface, wl_output: Option<WlOutput>);\n    fn unset_fullscreen(&mut self, wl_surface: WlSurface);\n    fn set_maximized(&mut self, wl_surface: WlSurface);\n    fn unset_maximized(&mut self, wl_surface: WlSurface);\n}\n\nstruct ToplevelData {\n    title: Option<String>,\n    app_id: Option<String>,\n    states: ArrayVec<u32, 3>,\n    output: Option<Output>,\n    instances: HashMap<ZwlrForeignToplevelHandleV1, Vec<WlOutput>>,\n    // FIXME: parent.\n}\n\npub struct ForeignToplevelGlobalData {\n    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,\n}\n\nimpl ForeignToplevelManagerState {\n    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self\n    where\n        D: GlobalDispatch<ZwlrForeignToplevelManagerV1, ForeignToplevelGlobalData>,\n        D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,\n        D: 'static,\n        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,\n    {\n        let global_data = ForeignToplevelGlobalData {\n            filter: Box::new(filter),\n        };\n        display.create_global::<D, ZwlrForeignToplevelManagerV1, _>(VERSION, global_data);\n        Self {\n            display: display.clone(),\n            instances: Vec::new(),\n            toplevels: HashMap::new(),\n        }\n    }\n}\n\npub fn refresh(state: &mut State) {\n    let _span = tracy_client::span!(\"foreign_toplevel::refresh\");\n\n    let protocol_state = &mut state.niri.foreign_toplevel_state;\n\n    // Handle closed windows.\n    protocol_state.toplevels.retain(|surface, data| {\n        if state.niri.layout.find_window_and_output(surface).is_some() {\n            return true;\n        }\n\n        for instance in data.instances.keys() {\n            instance.closed();\n        }\n\n        false\n    });\n\n    // Handle new and existing windows.\n    //\n    // Save the focused window for last, this way when the focus changes, we will first deactivate\n    // the previous window and only then activate the newly focused window.\n    let mut focused = None;\n    state.niri.layout.with_windows(|mapped, output, _, _| {\n        let toplevel = mapped.toplevel();\n        let wl_surface = toplevel.wl_surface();\n        with_toplevel_role_and_current(toplevel, |role, cur| {\n            let Some(cur) = cur else {\n                error!(\"mapped must have had initial commit\");\n                return;\n            };\n\n            if state.niri.keyboard_focus.surface() == Some(wl_surface) {\n                focused = Some((mapped.window.clone(), output.cloned()));\n            } else {\n                refresh_toplevel(protocol_state, wl_surface, role, cur, output, false);\n            }\n        });\n    });\n\n    // Finally, refresh the focused window.\n    if let Some((window, output)) = focused {\n        let toplevel = window.toplevel().expect(\"no X11 support\");\n        let wl_surface = toplevel.wl_surface();\n        with_toplevel_role_and_current(toplevel, |role, cur| {\n            let Some(cur) = cur else {\n                error!(\"mapped must have had initial commit\");\n                return;\n            };\n\n            refresh_toplevel(protocol_state, wl_surface, role, cur, output.as_ref(), true);\n        });\n    }\n}\n\npub fn on_output_bound(state: &mut State, output: &Output, wl_output: &WlOutput) {\n    let _span = tracy_client::span!(\"foreign_toplevel::on_output_bound\");\n\n    let Some(client) = wl_output.client() else {\n        return;\n    };\n\n    let protocol_state = &mut state.niri.foreign_toplevel_state;\n    for data in protocol_state.toplevels.values_mut() {\n        if data.output.as_ref() != Some(output) {\n            continue;\n        }\n\n        for (instance, outputs) in &mut data.instances {\n            if instance.client().as_ref() != Some(&client) {\n                continue;\n            }\n\n            instance.output_enter(wl_output);\n            instance.done();\n            outputs.push(wl_output.clone());\n        }\n    }\n}\n\nfn refresh_toplevel(\n    protocol_state: &mut ForeignToplevelManagerState,\n    wl_surface: &WlSurface,\n    role: &XdgToplevelSurfaceRoleAttributes,\n    current: &ToplevelState,\n    output: Option<&Output>,\n    has_focus: bool,\n) {\n    let states = to_state_vec(&current.states, has_focus);\n\n    match protocol_state.toplevels.entry(wl_surface.clone()) {\n        Entry::Occupied(entry) => {\n            // Existing window, check if anything changed.\n            let data = entry.into_mut();\n\n            let mut new_title = None;\n            if data.title != role.title {\n                data.title.clone_from(&role.title);\n                new_title = role.title.as_deref();\n\n                if new_title.is_none() {\n                    error!(\"toplevel title changed to None\");\n                }\n            }\n\n            let mut new_app_id = None;\n            if data.app_id != role.app_id {\n                data.app_id.clone_from(&role.app_id);\n                new_app_id = role.app_id.as_deref();\n\n                if new_app_id.is_none() {\n                    error!(\"toplevel app_id changed to None\");\n                }\n            }\n\n            let mut states_changed = false;\n            if data.states != states {\n                data.states = states;\n                states_changed = true;\n            }\n\n            let mut output_changed = false;\n            if data.output.as_ref() != output {\n                data.output = output.cloned();\n                output_changed = true;\n            }\n\n            let something_changed =\n                new_title.is_some() || new_app_id.is_some() || states_changed || output_changed;\n\n            if something_changed {\n                for (instance, outputs) in &mut data.instances {\n                    if let Some(new_title) = new_title {\n                        instance.title(new_title.to_owned());\n                    }\n                    if let Some(new_app_id) = new_app_id {\n                        instance.app_id(new_app_id.to_owned());\n                    }\n                    if states_changed {\n                        instance.state(data.states.iter().flat_map(|x| x.to_ne_bytes()).collect());\n                    }\n                    if output_changed {\n                        for wl_output in outputs.drain(..) {\n                            instance.output_leave(&wl_output);\n                        }\n                        if let Some(output) = &data.output {\n                            if let Some(client) = instance.client() {\n                                for wl_output in output.client_outputs(&client) {\n                                    instance.output_enter(&wl_output);\n                                    outputs.push(wl_output);\n                                }\n                            }\n                        }\n                    }\n                    instance.done();\n                }\n            }\n\n            for outputs in data.instances.values_mut() {\n                // Clean up dead wl_outputs.\n                outputs.retain(|x| x.is_alive());\n            }\n        }\n        Entry::Vacant(entry) => {\n            // New window, start tracking it.\n            let mut data = ToplevelData {\n                title: role.title.clone(),\n                app_id: role.app_id.clone(),\n                states,\n                output: output.cloned(),\n                instances: HashMap::new(),\n            };\n\n            for manager in &protocol_state.instances {\n                if let Some(client) = manager.client() {\n                    data.add_instance::<State>(&protocol_state.display, &client, manager);\n                }\n            }\n\n            entry.insert(data);\n        }\n    }\n}\n\nimpl ToplevelData {\n    fn add_instance<D>(\n        &mut self,\n        handle: &DisplayHandle,\n        client: &Client,\n        manager: &ZwlrForeignToplevelManagerV1,\n    ) where\n        D: Dispatch<ZwlrForeignToplevelHandleV1, ()>,\n        D: 'static,\n    {\n        let toplevel = client\n            .create_resource::<ZwlrForeignToplevelHandleV1, _, D>(handle, manager.version(), ())\n            .unwrap();\n        manager.toplevel(&toplevel);\n\n        if let Some(title) = &self.title {\n            toplevel.title(title.clone());\n        }\n        if let Some(app_id) = &self.app_id {\n            toplevel.app_id(app_id.clone());\n        }\n\n        toplevel.state(self.states.iter().flat_map(|x| x.to_ne_bytes()).collect());\n\n        let mut outputs = Vec::new();\n        if let Some(output) = &self.output {\n            for wl_output in output.client_outputs(client) {\n                toplevel.output_enter(&wl_output);\n                outputs.push(wl_output);\n            }\n        }\n\n        toplevel.done();\n\n        self.instances.insert(toplevel, outputs);\n    }\n}\n\nimpl<D> GlobalDispatch<ZwlrForeignToplevelManagerV1, ForeignToplevelGlobalData, D>\n    for ForeignToplevelManagerState\nwhere\n    D: GlobalDispatch<ZwlrForeignToplevelManagerV1, ForeignToplevelGlobalData>,\n    D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,\n    D: Dispatch<ZwlrForeignToplevelHandleV1, ()>,\n    D: ForeignToplevelHandler,\n{\n    fn bind(\n        state: &mut D,\n        handle: &DisplayHandle,\n        client: &Client,\n        resource: New<ZwlrForeignToplevelManagerV1>,\n        _global_data: &ForeignToplevelGlobalData,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        let manager = data_init.init(resource, ());\n\n        let state = state.foreign_toplevel_manager_state();\n\n        for data in state.toplevels.values_mut() {\n            data.add_instance::<D>(handle, client, &manager);\n        }\n\n        state.instances.push(manager);\n    }\n\n    fn can_view(client: Client, global_data: &ForeignToplevelGlobalData) -> bool {\n        (global_data.filter)(&client)\n    }\n}\n\nimpl<D> Dispatch<ZwlrForeignToplevelManagerV1, (), D> for ForeignToplevelManagerState\nwhere\n    D: Dispatch<ZwlrForeignToplevelManagerV1, ()>,\n    D: ForeignToplevelHandler,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        resource: &ZwlrForeignToplevelManagerV1,\n        request: <ZwlrForeignToplevelManagerV1 as Resource>::Request,\n        _data: &(),\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            zwlr_foreign_toplevel_manager_v1::Request::Stop => {\n                resource.finished();\n\n                let state = state.foreign_toplevel_manager_state();\n                state.instances.retain(|x| x != resource);\n            }\n            _ => unreachable!(),\n        }\n    }\n\n    fn destroyed(\n        state: &mut D,\n        _client: ClientId,\n        resource: &ZwlrForeignToplevelManagerV1,\n        _data: &(),\n    ) {\n        let state = state.foreign_toplevel_manager_state();\n        state.instances.retain(|x| x != resource);\n    }\n}\n\nimpl<D> Dispatch<ZwlrForeignToplevelHandleV1, (), D> for ForeignToplevelManagerState\nwhere\n    D: Dispatch<ZwlrForeignToplevelHandleV1, ()>,\n    D: ForeignToplevelHandler,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        resource: &ZwlrForeignToplevelHandleV1,\n        request: <ZwlrForeignToplevelHandleV1 as Resource>::Request,\n        _data: &(),\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        let protocol_state = state.foreign_toplevel_manager_state();\n\n        let Some((surface, _)) = protocol_state\n            .toplevels\n            .iter()\n            .find(|(_, data)| data.instances.contains_key(resource))\n        else {\n            return;\n        };\n        let surface = surface.clone();\n\n        match request {\n            zwlr_foreign_toplevel_handle_v1::Request::SetMaximized => state.set_maximized(surface),\n            zwlr_foreign_toplevel_handle_v1::Request::UnsetMaximized => {\n                state.unset_maximized(surface)\n            }\n            zwlr_foreign_toplevel_handle_v1::Request::SetMinimized => (),\n            zwlr_foreign_toplevel_handle_v1::Request::UnsetMinimized => (),\n            zwlr_foreign_toplevel_handle_v1::Request::Activate { .. } => {\n                state.activate(surface);\n            }\n            zwlr_foreign_toplevel_handle_v1::Request::Close => {\n                state.close(surface);\n            }\n            zwlr_foreign_toplevel_handle_v1::Request::SetRectangle { .. } => (),\n            zwlr_foreign_toplevel_handle_v1::Request::Destroy => (),\n            zwlr_foreign_toplevel_handle_v1::Request::SetFullscreen { output } => {\n                state.set_fullscreen(surface, output);\n            }\n            zwlr_foreign_toplevel_handle_v1::Request::UnsetFullscreen => {\n                state.unset_fullscreen(surface);\n            }\n            _ => unreachable!(),\n        }\n    }\n\n    fn destroyed(\n        state: &mut D,\n        _client: ClientId,\n        resource: &ZwlrForeignToplevelHandleV1,\n        _data: &(),\n    ) {\n        let state = state.foreign_toplevel_manager_state();\n        for data in state.toplevels.values_mut() {\n            data.instances.retain(|instance, _| instance != resource);\n        }\n    }\n}\n\nfn to_state_vec(states: &ToplevelStateSet, has_focus: bool) -> ArrayVec<u32, 3> {\n    let mut rv = ArrayVec::new();\n    if states.contains(xdg_toplevel::State::Maximized) {\n        rv.push(zwlr_foreign_toplevel_handle_v1::State::Maximized as u32);\n    }\n    if states.contains(xdg_toplevel::State::Fullscreen) {\n        rv.push(zwlr_foreign_toplevel_handle_v1::State::Fullscreen as u32);\n    }\n\n    // HACK: wlr-foreign-toplevel-management states:\n    //\n    // These have the same meaning as the states with the same names defined in xdg-toplevel\n    //\n    // However, clients such as sfwbar and fcitx seem to treat the activated state as keyboard\n    // focus, i.e. they don't expect multiple windows to have it set at once. Even Waybar which\n    // handles multiple activated windows correctly uses it in its design in such a way that\n    // keyboard focus would make more sense. Let's do what the clients expect.\n    if has_focus {\n        rv.push(zwlr_foreign_toplevel_handle_v1::State::Activated as u32);\n    }\n\n    rv\n}\n\n#[macro_export]\nmacro_rules! delegate_foreign_toplevel {\n    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {\n        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: $crate::protocols::foreign_toplevel::ForeignToplevelGlobalData\n        ] => $crate::protocols::foreign_toplevel::ForeignToplevelManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: ()\n        ] => $crate::protocols::foreign_toplevel::ForeignToplevelManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1: ()\n        ] => $crate::protocols::foreign_toplevel::ForeignToplevelManagerState);\n    };\n}\n"
  },
  {
    "path": "src/protocols/gamma_control.rs",
    "content": "use std::collections::HashMap;\nuse std::fs::File;\nuse std::io::Read;\n\nuse smithay::output::Output;\nuse smithay::reexports::wayland_protocols_wlr;\nuse smithay::reexports::wayland_server::backend::ClientId;\nuse smithay::reexports::wayland_server::{\n    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,\n};\nuse wayland_protocols_wlr::gamma_control::v1::server::{\n    zwlr_gamma_control_manager_v1, zwlr_gamma_control_v1,\n};\nuse zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1;\nuse zwlr_gamma_control_v1::ZwlrGammaControlV1;\n\nconst VERSION: u32 = 1;\n\npub struct GammaControlManagerState {\n    // Active gamma controls only. Failed ones are removed.\n    gamma_controls: HashMap<Output, ZwlrGammaControlV1>,\n}\n\npub struct GammaControlManagerGlobalData {\n    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,\n}\n\npub trait GammaControlHandler {\n    fn gamma_control_manager_state(&mut self) -> &mut GammaControlManagerState;\n    fn get_gamma_size(&mut self, output: &Output) -> Option<u32>;\n    fn set_gamma(&mut self, output: &Output, ramp: Option<Vec<u16>>) -> Option<()>;\n}\n\npub struct GammaControlState {\n    gamma_size: u32,\n}\n\nimpl GammaControlManagerState {\n    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self\n    where\n        D: GlobalDispatch<ZwlrGammaControlManagerV1, GammaControlManagerGlobalData>,\n        D: Dispatch<ZwlrGammaControlManagerV1, ()>,\n        D: Dispatch<ZwlrGammaControlV1, GammaControlState>,\n        D: GammaControlHandler,\n        D: 'static,\n        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,\n    {\n        let global_data = GammaControlManagerGlobalData {\n            filter: Box::new(filter),\n        };\n        display.create_global::<D, ZwlrGammaControlManagerV1, _>(VERSION, global_data);\n\n        Self {\n            gamma_controls: HashMap::new(),\n        }\n    }\n\n    pub fn output_removed(&mut self, output: &Output) {\n        if let Some(gamma_control) = self.gamma_controls.remove(output) {\n            gamma_control.failed();\n        }\n    }\n}\n\nimpl<D> GlobalDispatch<ZwlrGammaControlManagerV1, GammaControlManagerGlobalData, D>\n    for GammaControlManagerState\nwhere\n    D: GlobalDispatch<ZwlrGammaControlManagerV1, GammaControlManagerGlobalData>,\n    D: Dispatch<ZwlrGammaControlManagerV1, ()>,\n    D: Dispatch<ZwlrGammaControlV1, GammaControlState>,\n    D: GammaControlHandler,\n    D: 'static,\n{\n    fn bind(\n        _state: &mut D,\n        _handle: &DisplayHandle,\n        _client: &Client,\n        manager: New<ZwlrGammaControlManagerV1>,\n        _manager_state: &GammaControlManagerGlobalData,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        data_init.init(manager, ());\n    }\n\n    fn can_view(client: Client, global_data: &GammaControlManagerGlobalData) -> bool {\n        (global_data.filter)(&client)\n    }\n}\n\nimpl<D> Dispatch<ZwlrGammaControlManagerV1, (), D> for GammaControlManagerState\nwhere\n    D: Dispatch<ZwlrGammaControlManagerV1, ()>,\n    D: Dispatch<ZwlrGammaControlV1, GammaControlState>,\n    D: GammaControlHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        _resource: &ZwlrGammaControlManagerV1,\n        request: <ZwlrGammaControlManagerV1 as Resource>::Request,\n        _data: &(),\n        _dhandle: &DisplayHandle,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            zwlr_gamma_control_manager_v1::Request::GetGammaControl { id, output } => {\n                if let Some(output) = Output::from_resource(&output) {\n                    // We borrow state in the middle.\n                    #[allow(clippy::map_entry)]\n                    if !state\n                        .gamma_control_manager_state()\n                        .gamma_controls\n                        .contains_key(&output)\n                    {\n                        if let Some(gamma_size) = state.get_gamma_size(&output) {\n                            let zwlr_gamma_control =\n                                data_init.init(id, GammaControlState { gamma_size });\n                            zwlr_gamma_control.gamma_size(gamma_size);\n                            state\n                                .gamma_control_manager_state()\n                                .gamma_controls\n                                .insert(output, zwlr_gamma_control);\n                            return;\n                        }\n                    }\n                }\n\n                data_init\n                    .init(id, GammaControlState { gamma_size: 0 })\n                    .failed();\n            }\n            zwlr_gamma_control_manager_v1::Request::Destroy => (),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl<D> Dispatch<ZwlrGammaControlV1, GammaControlState, D> for GammaControlManagerState\nwhere\n    D: Dispatch<ZwlrGammaControlV1, GammaControlState>,\n    D: GammaControlHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        resource: &ZwlrGammaControlV1,\n        request: <ZwlrGammaControlV1 as Resource>::Request,\n        data: &GammaControlState,\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            zwlr_gamma_control_v1::Request::SetGamma { fd } => {\n                let gamma_controls = &mut state.gamma_control_manager_state().gamma_controls;\n                let Some((output, _)) = gamma_controls.iter().find(|(_, x)| *x == resource) else {\n                    return;\n                };\n                let output = output.clone();\n\n                trace!(\"setting gamma for output {}\", output.name());\n\n                // Start with a u16 slice so it's aligned correctly.\n                let mut gamma = vec![0u16; data.gamma_size as usize * 3];\n                let buf = bytemuck::cast_slice_mut(&mut gamma);\n                let mut file = File::from(fd);\n                {\n                    let _span = tracy_client::span!(\"read gamma from fd\");\n\n                    if let Err(err) = file.read_exact(buf) {\n                        warn!(\"failed to read gamma data: {err:?}\");\n                        resource.failed();\n                        gamma_controls.remove(&output);\n                        let _ = state.set_gamma(&output, None);\n                        return;\n                    }\n\n                    // Verify that there's no more data.\n                    {\n                        match file.read(&mut [0]) {\n                            Ok(0) => (),\n                            Ok(_) => {\n                                warn!(\"gamma data is too large\");\n                                resource.failed();\n                                gamma_controls.remove(&output);\n                                let _ = state.set_gamma(&output, None);\n                                return;\n                            }\n                            Err(err) => {\n                                warn!(\"error reading gamma data: {err:?}\");\n                                resource.failed();\n                                gamma_controls.remove(&output);\n                                let _ = state.set_gamma(&output, None);\n                                return;\n                            }\n                        }\n                    }\n                }\n\n                if state.set_gamma(&output, Some(gamma)).is_none() {\n                    resource.failed();\n                    let gamma_controls = &mut state.gamma_control_manager_state().gamma_controls;\n                    gamma_controls.remove(&output);\n                    let _ = state.set_gamma(&output, None);\n                }\n            }\n            zwlr_gamma_control_v1::Request::Destroy => (),\n            _ => unreachable!(),\n        }\n    }\n\n    fn destroyed(\n        state: &mut D,\n        _client: ClientId,\n        resource: &ZwlrGammaControlV1,\n        _data: &GammaControlState,\n    ) {\n        let gamma_controls = &mut state.gamma_control_manager_state().gamma_controls;\n        let Some((output, _)) = gamma_controls.iter().find(|(_, x)| *x == resource) else {\n            return;\n        };\n        let output = output.clone();\n        gamma_controls.remove(&output);\n\n        let _ = state.set_gamma(&output, None);\n    }\n}\n\n#[macro_export]\nmacro_rules! delegate_gamma_control {\n    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {\n        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::gamma_control::v1::server::zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1: $crate::protocols::gamma_control::GammaControlManagerGlobalData\n        ] => $crate::protocols::gamma_control::GammaControlManagerState);\n\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::gamma_control::v1::server::zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1: ()\n        ] => $crate::protocols::gamma_control::GammaControlManagerState);\n\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::gamma_control::v1::server::zwlr_gamma_control_v1::ZwlrGammaControlV1:  $crate::protocols::gamma_control::GammaControlState\n        ] => $crate::protocols::gamma_control::GammaControlManagerState);\n    };\n}\n"
  },
  {
    "path": "src/protocols/mod.rs",
    "content": "pub mod ext_workspace;\npub mod foreign_toplevel;\npub mod gamma_control;\npub mod mutter_x11_interop;\npub mod output_management;\npub mod screencopy;\npub mod virtual_pointer;\n\npub mod raw;\n"
  },
  {
    "path": "src/protocols/mutter_x11_interop.rs",
    "content": "use mutter_x11_interop::MutterX11Interop;\nuse smithay::reexports::wayland_server::{\n    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,\n};\n\nuse super::raw::mutter_x11_interop::v1::server::mutter_x11_interop;\n\nconst VERSION: u32 = 1;\n\npub struct MutterX11InteropManagerState {}\n\npub struct MutterX11InteropManagerGlobalData {\n    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,\n}\n\npub trait MutterX11InteropHandler {}\n\nimpl MutterX11InteropManagerState {\n    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self\n    where\n        D: GlobalDispatch<MutterX11Interop, MutterX11InteropManagerGlobalData>,\n        D: Dispatch<MutterX11Interop, ()>,\n        D: MutterX11InteropHandler,\n        D: 'static,\n        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,\n    {\n        let global_data = MutterX11InteropManagerGlobalData {\n            filter: Box::new(filter),\n        };\n        display.create_global::<D, MutterX11Interop, _>(VERSION, global_data);\n\n        Self {}\n    }\n}\n\nimpl<D> GlobalDispatch<MutterX11Interop, MutterX11InteropManagerGlobalData, D>\n    for MutterX11InteropManagerState\nwhere\n    D: GlobalDispatch<MutterX11Interop, MutterX11InteropManagerGlobalData>,\n    D: Dispatch<MutterX11Interop, ()>,\n    D: MutterX11InteropHandler,\n    D: 'static,\n{\n    fn bind(\n        _state: &mut D,\n        _handle: &DisplayHandle,\n        _client: &Client,\n        manager: New<MutterX11Interop>,\n        _manager_state: &MutterX11InteropManagerGlobalData,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        data_init.init(manager, ());\n    }\n\n    fn can_view(client: Client, global_data: &MutterX11InteropManagerGlobalData) -> bool {\n        (global_data.filter)(&client)\n    }\n}\n\nimpl<D> Dispatch<MutterX11Interop, (), D> for MutterX11InteropManagerState\nwhere\n    D: Dispatch<MutterX11Interop, ()>,\n    D: MutterX11InteropHandler,\n    D: 'static,\n{\n    fn request(\n        _state: &mut D,\n        _client: &Client,\n        _resource: &MutterX11Interop,\n        request: <MutterX11Interop as Resource>::Request,\n        _data: &(),\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            mutter_x11_interop::Request::Destroy => (),\n            mutter_x11_interop::Request::SetX11Parent { .. } => (),\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! delegate_mutter_x11_interop {\n    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {\n        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            $crate::protocols::raw::mutter_x11_interop::v1::server::mutter_x11_interop::MutterX11Interop: $crate::protocols::mutter_x11_interop::MutterX11InteropManagerGlobalData\n        ] => $crate::protocols::mutter_x11_interop::MutterX11InteropManagerState);\n\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            $crate::protocols::raw::mutter_x11_interop::v1::server::mutter_x11_interop::MutterX11Interop: ()\n        ] => $crate::protocols::mutter_x11_interop::MutterX11InteropManagerState);\n    };\n}\n"
  },
  {
    "path": "src/protocols/output_management.rs",
    "content": "use std::collections::hash_map::Entry;\nuse std::collections::HashMap;\nuse std::iter::zip;\nuse std::mem;\n\nuse niri_config::{FloatOrInt, OutputName, Vrr};\nuse niri_ipc::Transform;\nuse smithay::reexports::wayland_protocols_wlr::output_management::v1::server::{\n    zwlr_output_configuration_head_v1, zwlr_output_configuration_v1, zwlr_output_head_v1,\n    zwlr_output_manager_v1, zwlr_output_mode_v1,\n};\nuse smithay::reexports::wayland_server::backend::ClientId;\nuse smithay::reexports::wayland_server::protocol::wl_output::Transform as WlTransform;\nuse smithay::reexports::wayland_server::{\n    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, WEnum,\n};\nuse zwlr_output_configuration_head_v1::ZwlrOutputConfigurationHeadV1;\nuse zwlr_output_configuration_v1::ZwlrOutputConfigurationV1;\nuse zwlr_output_head_v1::{AdaptiveSyncState, ZwlrOutputHeadV1};\nuse zwlr_output_manager_v1::ZwlrOutputManagerV1;\nuse zwlr_output_mode_v1::ZwlrOutputModeV1;\n\nuse crate::backend::OutputId;\nuse crate::niri::State;\nuse crate::utils::ipc_transform_to_smithay;\n\nconst VERSION: u32 = 4;\n\n#[derive(Debug)]\nstruct ClientData {\n    heads: HashMap<OutputId, (ZwlrOutputHeadV1, Vec<ZwlrOutputModeV1>)>,\n    confs: HashMap<ZwlrOutputConfigurationV1, OutputConfigurationState>,\n    manager: ZwlrOutputManagerV1,\n}\n\npub struct OutputManagementManagerState {\n    display: DisplayHandle,\n    serial: u32,\n    clients: HashMap<ClientId, ClientData>,\n    current_state: HashMap<OutputId, niri_ipc::Output>,\n    current_config: niri_config::Outputs,\n}\n\npub struct OutputManagementManagerGlobalData {\n    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,\n}\n\npub trait OutputManagementHandler {\n    fn output_management_state(&mut self) -> &mut OutputManagementManagerState;\n    fn apply_output_config(&mut self, config: niri_config::Outputs);\n}\n\n#[derive(Debug)]\nenum OutputConfigurationState {\n    Ongoing(HashMap<OutputId, niri_config::Output>),\n    Finished,\n}\n\npub enum OutputConfigurationHeadState {\n    Cancelled,\n    Ok(OutputId, ZwlrOutputConfigurationV1),\n}\n\nimpl OutputManagementManagerState {\n    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self\n    where\n        D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n        D: Dispatch<ZwlrOutputManagerV1, ()>,\n        D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n        D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n        D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n        D: Dispatch<ZwlrOutputModeV1, ()>,\n        D: OutputManagementHandler,\n        D: 'static,\n        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,\n    {\n        let global_data = OutputManagementManagerGlobalData {\n            filter: Box::new(filter),\n        };\n        display.create_global::<D, ZwlrOutputManagerV1, _>(VERSION, global_data);\n\n        Self {\n            display: display.clone(),\n            clients: HashMap::new(),\n            serial: 0,\n            current_state: HashMap::new(),\n            current_config: Default::default(),\n        }\n    }\n\n    pub fn on_config_changed(&mut self, new_config: niri_config::Outputs) {\n        self.current_config = new_config;\n    }\n\n    pub fn notify_changes(&mut self, new_state: HashMap<OutputId, niri_ipc::Output>) {\n        let mut changed = false; /* most likely to end up true */\n        for (output, conf) in new_state.iter() {\n            if let Some(old) = self.current_state.get(output) {\n                if old.vrr_enabled != conf.vrr_enabled {\n                    changed = true;\n                    for client in self.clients.values() {\n                        if let Some((head, _)) = client.heads.get(output) {\n                            if head.version() >= zwlr_output_head_v1::EVT_ADAPTIVE_SYNC_SINCE {\n                                head.adaptive_sync(match conf.vrr_enabled {\n                                    true => AdaptiveSyncState::Enabled,\n                                    false => AdaptiveSyncState::Disabled,\n                                });\n                            }\n                        }\n                    }\n                }\n\n                // Winit and virtual outputs can change modes; on a TTY custom modes can add/remove\n                // a mode.\n                let modes_changed = old.modes != conf.modes;\n                if modes_changed {\n                    changed = true;\n                    for client in self.clients.values_mut() {\n                        if let Some((head, modes)) = client.heads.get_mut(output) {\n                            // Ends on the shortest iterator.\n                            let zwlr_modes_with_modes = zip(modes.iter(), &conf.modes);\n                            let least_modes_len = zwlr_modes_with_modes.len();\n\n                            for (wl_mode, mode) in zwlr_modes_with_modes {\n                                wl_mode.size(i32::from(mode.width), i32::from(mode.height));\n                                if let Ok(refresh_rate) = mode.refresh_rate.try_into() {\n                                    wl_mode.refresh(refresh_rate);\n                                }\n                            }\n\n                            if let Some(client) = client.manager.client() {\n                                if conf.modes.len() > least_modes_len {\n                                    for mode in &conf.modes[least_modes_len..] {\n                                        // One or more modes were added.\n                                        let new_mode = client\n                                            .create_resource::<ZwlrOutputModeV1, _, State>(\n                                                &self.display,\n                                                head.version(),\n                                                (),\n                                            )\n                                            .unwrap();\n                                        head.mode(&new_mode);\n                                        new_mode\n                                            .size(i32::from(mode.width), i32::from(mode.height));\n                                        if let Ok(refresh_rate) = mode.refresh_rate.try_into() {\n                                            new_mode.refresh(refresh_rate)\n                                        }\n                                        modes.push(new_mode);\n                                    }\n                                } else if modes.len() > least_modes_len {\n                                    // One or more modes were removed.\n                                    for mode in modes.drain(least_modes_len..) {\n                                        mode.finished();\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n\n                match (old.current_mode, conf.current_mode) {\n                    (Some(old_index), Some(new_index)) => {\n                        if old.modes.len() == conf.modes.len()\n                            && (modes_changed || old_index != new_index)\n                        {\n                            changed = true;\n                            for client in self.clients.values() {\n                                if let Some((head, modes)) = client.heads.get(output) {\n                                    if let Some(new_mode) = modes.get(new_index) {\n                                        head.current_mode(new_mode);\n                                    } else {\n                                        error!(\n                                            \"output new mode doesnt exist for the client's output\"\n                                        );\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    (Some(_), None) => {\n                        changed = true;\n                        for client in self.clients.values() {\n                            if let Some((head, _)) = client.heads.get(output) {\n                                head.enabled(0);\n                            }\n                        }\n                    }\n                    (None, Some(new_index)) => {\n                        if old.modes.len() == conf.modes.len() {\n                            changed = true;\n                            for client in self.clients.values() {\n                                if let Some((head, modes)) = client.heads.get(output) {\n                                    head.enabled(1);\n                                    if let Some(mode) = modes.get(new_index) {\n                                        head.current_mode(mode);\n                                    } else {\n                                        error!(\n                                            \"output new mode doesnt exist for the client's output\"\n                                        );\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    (None, None) => {}\n                }\n                match (old.logical, conf.logical) {\n                    (Some(old_logical), Some(new_logical)) => {\n                        if old_logical != new_logical {\n                            changed = true;\n                            for client in self.clients.values() {\n                                if let Some((head, _)) = client.heads.get(output) {\n                                    if old_logical.x != new_logical.x\n                                        || old_logical.y != new_logical.y\n                                    {\n                                        head.position(new_logical.x, new_logical.y);\n                                    }\n                                    if old_logical.scale != new_logical.scale {\n                                        head.scale(new_logical.scale);\n                                    }\n                                    if old_logical.transform != new_logical.transform {\n                                        head.transform(\n                                            ipc_transform_to_smithay(new_logical.transform).into(),\n                                        );\n                                    }\n                                }\n                            }\n                        }\n                    }\n                    (None, Some(new_logical)) => {\n                        changed = true;\n                        for client in self.clients.values() {\n                            if let Some((head, _)) = client.heads.get(output) {\n                                // head enable in the mode diff check\n                                head.position(new_logical.x, new_logical.y);\n                                head.transform(\n                                    ipc_transform_to_smithay(new_logical.transform).into(),\n                                );\n                                head.scale(new_logical.scale);\n                            }\n                        }\n                    }\n                    (Some(_), None) => {\n                        // heads disabled in the mode diff check\n                    }\n                    (None, None) => {}\n                }\n            } else {\n                changed = true;\n                notify_new_head(self, output, conf);\n            }\n        }\n        for (old, _) in self.current_state.iter() {\n            if !new_state.contains_key(old) {\n                changed = true;\n                notify_removed_head(&mut self.clients, old);\n            }\n        }\n        if changed {\n            self.current_state = new_state;\n            self.serial += 1;\n            for data in self.clients.values() {\n                data.manager.done(self.serial);\n                for conf in data.confs.keys() {\n                    conf.cancelled();\n                }\n            }\n        }\n    }\n}\n\nimpl<D> GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData, D>\n    for OutputManagementManagerState\nwhere\n    D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n    D: Dispatch<ZwlrOutputManagerV1, ()>,\n    D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n    D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n    D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: OutputManagementHandler,\n    D: 'static,\n{\n    fn bind(\n        state: &mut D,\n        display: &DisplayHandle,\n        client: &Client,\n        manager: New<ZwlrOutputManagerV1>,\n        _manager_state: &OutputManagementManagerGlobalData,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        let manager = data_init.init(manager, ());\n        let g_state = state.output_management_state();\n        let mut client_data = ClientData {\n            heads: HashMap::new(),\n            confs: HashMap::new(),\n            manager: manager.clone(),\n        };\n        for (output, conf) in &g_state.current_state {\n            send_new_head::<D>(display, client, &mut client_data, *output, conf);\n        }\n        g_state.clients.insert(client.id(), client_data);\n        manager.done(g_state.serial);\n    }\n\n    fn can_view(client: Client, global_data: &OutputManagementManagerGlobalData) -> bool {\n        (global_data.filter)(&client)\n    }\n}\n\nimpl<D> Dispatch<ZwlrOutputManagerV1, (), D> for OutputManagementManagerState\nwhere\n    D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n    D: Dispatch<ZwlrOutputManagerV1, ()>,\n    D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n    D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n    D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: OutputManagementHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        client: &Client,\n        _manager: &ZwlrOutputManagerV1,\n        request: zwlr_output_manager_v1::Request,\n        _data: &(),\n        _display: &DisplayHandle,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            zwlr_output_manager_v1::Request::CreateConfiguration { id, serial } => {\n                let g_state = state.output_management_state();\n                let conf = data_init.init(id, serial);\n                if let Some(client_data) = g_state.clients.get_mut(&client.id()) {\n                    if serial != g_state.serial {\n                        conf.cancelled();\n                    }\n                    let state = OutputConfigurationState::Ongoing(HashMap::new());\n                    client_data.confs.insert(conf, state);\n                } else {\n                    error!(\"CreateConfiguration: missing client data\");\n                }\n            }\n            zwlr_output_manager_v1::Request::Stop => {\n                if let Some(c) = state.output_management_state().clients.remove(&client.id()) {\n                    c.manager.finished()\n                }\n            }\n            _ => unreachable!(),\n        }\n    }\n    fn destroyed(state: &mut D, client: ClientId, _resource: &ZwlrOutputManagerV1, _data: &()) {\n        state.output_management_state().clients.remove(&client);\n    }\n}\n\nimpl<D> Dispatch<ZwlrOutputConfigurationV1, u32, D> for OutputManagementManagerState\nwhere\n    D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n    D: Dispatch<ZwlrOutputManagerV1, ()>,\n    D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n    D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n    D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: OutputManagementHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        client: &Client,\n        conf: &ZwlrOutputConfigurationV1,\n        request: zwlr_output_configuration_v1::Request,\n        serial: &u32,\n        _display: &DisplayHandle,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        let g_state = state.output_management_state();\n        let outdated = *serial != g_state.serial;\n        if outdated {\n            debug!(\"OutputConfiguration: request from an outdated configuration\");\n        }\n\n        let new_config = g_state\n            .clients\n            .get_mut(&client.id())\n            .and_then(|data| data.confs.get_mut(conf));\n        if new_config.is_none() {\n            error!(\"OutputConfiguration: request from unknown configuration object\");\n        }\n\n        match request {\n            zwlr_output_configuration_v1::Request::EnableHead { id, head } => {\n                let Some(output) = head.data::<OutputId>() else {\n                    error!(\"EnableHead: Missing attached output\");\n                    let _fail = data_init.init(id, OutputConfigurationHeadState::Cancelled);\n                    return;\n                };\n                if outdated {\n                    let _fail = data_init.init(id, OutputConfigurationHeadState::Cancelled);\n                    return;\n                }\n\n                let Some(new_config) = new_config else {\n                    let _fail = data_init.init(id, OutputConfigurationHeadState::Cancelled);\n                    return;\n                };\n\n                let OutputConfigurationState::Ongoing(new_config) = new_config else {\n                    let _fail = data_init.init(id, OutputConfigurationHeadState::Cancelled);\n                    conf.post_error(\n                        zwlr_output_configuration_v1::Error::AlreadyUsed,\n                        \"configuration had already been used\",\n                    );\n                    return;\n                };\n\n                let Some(current_config) = g_state.current_state.get(output) else {\n                    error!(\"EnableHead: output missing from current config\");\n                    let _fail = data_init.init(id, OutputConfigurationHeadState::Cancelled);\n                    return;\n                };\n\n                match new_config.entry(*output) {\n                    Entry::Occupied(_) => {\n                        let _fail = data_init.init(id, OutputConfigurationHeadState::Cancelled);\n                        conf.post_error(\n                            zwlr_output_configuration_v1::Error::AlreadyConfiguredHead,\n                            \"head has been already configured\",\n                        );\n                        return;\n                    }\n                    Entry::Vacant(entry) => {\n                        let name = OutputName::from_ipc_output(current_config);\n                        let mut config = g_state\n                            .current_config\n                            .find(&name)\n                            .cloned()\n                            .unwrap_or_else(|| niri_config::Output {\n                                name: name.format_make_model_serial_or_connector(),\n                                ..Default::default()\n                            });\n                        config.off = false;\n                        entry.insert(config);\n                    }\n                };\n\n                data_init.init(id, OutputConfigurationHeadState::Ok(*output, conf.clone()));\n            }\n            zwlr_output_configuration_v1::Request::DisableHead { head } => {\n                if outdated {\n                    return;\n                }\n                let Some(output) = head.data::<OutputId>() else {\n                    error!(\"DisableHead: missing attached output head name\");\n                    return;\n                };\n\n                let Some(new_config) = new_config else {\n                    return;\n                };\n\n                let OutputConfigurationState::Ongoing(new_config) = new_config else {\n                    conf.post_error(\n                        zwlr_output_configuration_v1::Error::AlreadyUsed,\n                        \"configuration had already been used\",\n                    );\n                    return;\n                };\n\n                let Some(current_config) = g_state.current_state.get(output) else {\n                    error!(\"EnableHead: output missing from current config\");\n                    return;\n                };\n\n                match new_config.entry(*output) {\n                    Entry::Occupied(_) => {\n                        conf.post_error(\n                            zwlr_output_configuration_v1::Error::AlreadyConfiguredHead,\n                            \"head has been already configured\",\n                        );\n                    }\n                    Entry::Vacant(entry) => {\n                        let name = OutputName::from_ipc_output(current_config);\n                        let mut config = g_state\n                            .current_config\n                            .find(&name)\n                            .cloned()\n                            .unwrap_or_else(|| niri_config::Output {\n                                name: name.format_make_model_serial_or_connector(),\n                                ..Default::default()\n                            });\n                        config.off = true;\n                        entry.insert(config);\n                    }\n                };\n            }\n            zwlr_output_configuration_v1::Request::Apply => {\n                if outdated {\n                    conf.cancelled();\n                    return;\n                }\n\n                let Some(new_config) = new_config else {\n                    return;\n                };\n\n                let OutputConfigurationState::Ongoing(new_config) =\n                    mem::replace(new_config, OutputConfigurationState::Finished)\n                else {\n                    conf.post_error(\n                        zwlr_output_configuration_v1::Error::AlreadyUsed,\n                        \"configuration had already been used\",\n                    );\n                    return;\n                };\n\n                let any_enabled = new_config.values().any(|c| !c.off);\n                if !any_enabled {\n                    conf.failed();\n                    return;\n                }\n\n                state.apply_output_config(new_config.into_values().collect());\n                // FIXME: verify that it had been applied successfully (which may be difficult).\n                conf.succeeded();\n            }\n            zwlr_output_configuration_v1::Request::Test => {\n                if outdated {\n                    conf.cancelled();\n                    return;\n                }\n\n                let Some(new_config) = new_config else {\n                    return;\n                };\n\n                let OutputConfigurationState::Ongoing(new_config) =\n                    mem::replace(new_config, OutputConfigurationState::Finished)\n                else {\n                    conf.post_error(\n                        zwlr_output_configuration_v1::Error::AlreadyUsed,\n                        \"configuration had already been used\",\n                    );\n                    return;\n                };\n\n                let any_enabled = new_config.values().any(|c| !c.off);\n                if !any_enabled {\n                    conf.failed();\n                    return;\n                }\n\n                // FIXME: actually test the configuration with TTY.\n                conf.succeeded()\n            }\n            zwlr_output_configuration_v1::Request::Destroy => {\n                g_state\n                    .clients\n                    .get_mut(&client.id())\n                    .map(|d| d.confs.remove(conf));\n            }\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl<D> Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState, D>\n    for OutputManagementManagerState\nwhere\n    D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n    D: Dispatch<ZwlrOutputManagerV1, ()>,\n    D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n    D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n    D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: OutputManagementHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        client: &Client,\n        conf_head: &ZwlrOutputConfigurationHeadV1,\n        request: zwlr_output_configuration_head_v1::Request,\n        data: &OutputConfigurationHeadState,\n        _display: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        let g_state = state.output_management_state();\n        let Some(client_data) = g_state.clients.get_mut(&client.id()) else {\n            error!(\"ConfigurationHead: missing client data\");\n            return;\n        };\n        let OutputConfigurationHeadState::Ok(output_id, conf) = data else {\n            warn!(\"ConfigurationHead: request sent to a cancelled head\");\n            return;\n        };\n        let Some(serial) = conf.data::<u32>() else {\n            error!(\"ConfigurationHead: missing serial\");\n            return;\n        };\n        if *serial != g_state.serial {\n            warn!(\"ConfigurationHead: request sent to an outdated\");\n            return;\n        }\n        let Some(new_config) = client_data.confs.get_mut(conf) else {\n            error!(\"ConfigurationHead: unknown configuration\");\n            return;\n        };\n        let OutputConfigurationState::Ongoing(new_config) = new_config else {\n            conf.post_error(\n                zwlr_output_configuration_v1::Error::AlreadyUsed,\n                \"configuration had already been used\",\n            );\n            return;\n        };\n        let Some(new_config) = new_config.get_mut(output_id) else {\n            error!(\"ConfigurationHead: config missing from enabled heads\");\n            return;\n        };\n\n        match request {\n            zwlr_output_configuration_head_v1::Request::SetMode { mode } => {\n                let index = match client_data\n                    .heads\n                    .get(output_id)\n                    .map(|(_, mods)| mods.iter().position(|m| m.id() == mode.id()))\n                {\n                    Some(Some(index)) => index,\n                    _ => {\n                        warn!(\"SetMode: failed to find requested mode\");\n                        conf_head.post_error(\n                            zwlr_output_configuration_head_v1::Error::InvalidMode,\n                            \"failed to find requested mode\",\n                        );\n                        return;\n                    }\n                };\n\n                let Some(current_config) = g_state.current_state.get(output_id) else {\n                    warn!(\"SetMode: output missing from the current config\");\n                    return;\n                };\n\n                let Some(mode) = current_config.modes.get(index) else {\n                    error!(\"SetMode: requested mode is out of range\");\n                    return;\n                };\n\n                new_config.mode = Some(niri_config::output::Mode {\n                    custom: false,\n                    mode: niri_ipc::ConfiguredMode {\n                        width: mode.width,\n                        height: mode.height,\n                        refresh: Some(mode.refresh_rate as f64 / 1000.),\n                    },\n                });\n                new_config.modeline = None;\n            }\n            zwlr_output_configuration_head_v1::Request::SetCustomMode {\n                width,\n                height,\n                refresh,\n            } => {\n                let (width, height, refresh): (u16, u16, u32) =\n                    match (width.try_into(), height.try_into(), refresh.try_into()) {\n                        (Ok(width), Ok(height), Ok(refresh)) => (width, height, refresh),\n                        _ => {\n                            warn!(\"SetCustomMode: invalid input data\");\n                            return;\n                        }\n                    };\n\n                if refresh == 0 {\n                    warn!(\"SetCustomMode: refresh 0 requested, ignoring\");\n                    return;\n                }\n\n                new_config.mode = Some(niri_config::output::Mode {\n                    custom: true,\n                    mode: niri_ipc::ConfiguredMode {\n                        width,\n                        height,\n                        refresh: Some(refresh as f64 / 1000.),\n                    },\n                });\n                new_config.modeline = None;\n            }\n            zwlr_output_configuration_head_v1::Request::SetPosition { x, y } => {\n                new_config.position = Some(niri_config::Position { x, y });\n            }\n            zwlr_output_configuration_head_v1::Request::SetTransform { transform } => {\n                let transform = match transform {\n                    WEnum::Value(WlTransform::Normal) => Transform::Normal,\n                    WEnum::Value(WlTransform::_90) => Transform::_90,\n                    WEnum::Value(WlTransform::_180) => Transform::_180,\n                    WEnum::Value(WlTransform::_270) => Transform::_270,\n                    WEnum::Value(WlTransform::Flipped) => Transform::Flipped,\n                    WEnum::Value(WlTransform::Flipped90) => Transform::Flipped90,\n                    WEnum::Value(WlTransform::Flipped180) => Transform::Flipped180,\n                    WEnum::Value(WlTransform::Flipped270) => Transform::Flipped270,\n                    _ => {\n                        warn!(\"SetTransform: unknown requested transform\");\n                        conf_head.post_error(\n                            zwlr_output_configuration_head_v1::Error::InvalidTransform,\n                            \"unknown transform value\",\n                        );\n                        return;\n                    }\n                };\n                new_config.transform = transform;\n            }\n            zwlr_output_configuration_head_v1::Request::SetScale { scale } => {\n                if scale <= 0. {\n                    conf_head.post_error(\n                        zwlr_output_configuration_head_v1::Error::InvalidScale,\n                        \"scale is negative or zero\",\n                    );\n                    return;\n                }\n                new_config.scale = Some(FloatOrInt(scale));\n            }\n            zwlr_output_configuration_head_v1::Request::SetAdaptiveSync { state } => {\n                let vrr = match state {\n                    WEnum::Value(AdaptiveSyncState::Enabled) => Some(Vrr { on_demand: false }),\n                    WEnum::Value(AdaptiveSyncState::Disabled) => None,\n                    _ => {\n                        warn!(\"SetAdaptativeSync: unknown requested adaptative sync\");\n                        conf_head.post_error(\n                            zwlr_output_configuration_head_v1::Error::InvalidAdaptiveSyncState,\n                            \"unknown adaptive sync value\",\n                        );\n                        return;\n                    }\n                };\n                new_config.variable_refresh_rate = vrr;\n            }\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl<D> Dispatch<ZwlrOutputHeadV1, OutputId, D> for OutputManagementManagerState\nwhere\n    D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n    D: Dispatch<ZwlrOutputManagerV1, ()>,\n    D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n    D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n    D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: OutputManagementHandler,\n    D: 'static,\n{\n    fn request(\n        _state: &mut D,\n        _client: &Client,\n        _output_head: &ZwlrOutputHeadV1,\n        request: zwlr_output_head_v1::Request,\n        _data: &OutputId,\n        _display: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            zwlr_output_head_v1::Request::Release => {}\n            _ => unreachable!(),\n        }\n    }\n    fn destroyed(state: &mut D, client: ClientId, _resource: &ZwlrOutputHeadV1, data: &OutputId) {\n        if let Some(c) = state.output_management_state().clients.get_mut(&client) {\n            c.heads.remove(data);\n        }\n    }\n}\n\nimpl<D> Dispatch<ZwlrOutputModeV1, (), D> for OutputManagementManagerState\nwhere\n    D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n    D: Dispatch<ZwlrOutputManagerV1, ()>,\n    D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n    D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n    D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: OutputManagementHandler,\n    D: 'static,\n{\n    fn request(\n        _state: &mut D,\n        _client: &Client,\n        _mode: &ZwlrOutputModeV1,\n        request: zwlr_output_mode_v1::Request,\n        _data: &(),\n        _display: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        match request {\n            zwlr_output_mode_v1::Request::Release => {}\n            _ => unreachable!(),\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! delegate_output_management{\n    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {\n        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_manager_v1::ZwlrOutputManagerV1: $crate::protocols::output_management::OutputManagementManagerGlobalData\n        ] => $crate::protocols::output_management::OutputManagementManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_manager_v1::ZwlrOutputManagerV1: ()\n        ] => $crate::protocols::output_management::OutputManagementManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_configuration_v1::ZwlrOutputConfigurationV1: u32\n        ] => $crate::protocols::output_management::OutputManagementManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_head_v1::ZwlrOutputHeadV1: $crate::backend::OutputId\n        ] => $crate::protocols::output_management::OutputManagementManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_mode_v1::ZwlrOutputModeV1: ()\n        ] => $crate::protocols::output_management::OutputManagementManagerState);\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::output_management::v1::server::zwlr_output_configuration_head_v1::ZwlrOutputConfigurationHeadV1: $crate::protocols::output_management::OutputConfigurationHeadState\n        ] => $crate::protocols::output_management::OutputManagementManagerState);\n    };\n}\n\nfn notify_removed_head(clients: &mut HashMap<ClientId, ClientData>, head: &OutputId) {\n    for data in clients.values_mut() {\n        if let Some((head, mods)) = data.heads.remove(head) {\n            mods.iter().for_each(|m| m.finished());\n            head.finished();\n        }\n    }\n}\n\nfn notify_new_head(\n    state: &mut OutputManagementManagerState,\n    output: &OutputId,\n    conf: &niri_ipc::Output,\n) {\n    let display = &state.display;\n    let clients = &mut state.clients;\n    for data in clients.values_mut() {\n        if let Some(client) = data.manager.client() {\n            send_new_head::<State>(display, &client, data, *output, conf);\n        }\n    }\n}\n\nfn send_new_head<D>(\n    display: &DisplayHandle,\n    client: &Client,\n    client_data: &mut ClientData,\n    output: OutputId,\n    conf: &niri_ipc::Output,\n) where\n    D: GlobalDispatch<ZwlrOutputManagerV1, OutputManagementManagerGlobalData>,\n    D: Dispatch<ZwlrOutputManagerV1, ()>,\n    D: Dispatch<ZwlrOutputConfigurationV1, u32>,\n    D: Dispatch<ZwlrOutputConfigurationHeadV1, OutputConfigurationHeadState>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: OutputManagementHandler,\n    D: 'static,\n    D: Dispatch<ZwlrOutputHeadV1, OutputId>,\n    D: Dispatch<ZwlrOutputModeV1, ()>,\n    D: 'static,\n{\n    let new_head = client\n        .create_resource::<ZwlrOutputHeadV1, _, D>(display, client_data.manager.version(), output)\n        .unwrap();\n    client_data.manager.head(&new_head);\n    new_head.name(conf.name.clone());\n    // Format matches what Output::new() does internally.\n    new_head.description(format!(\"{} - {} - {}\", conf.make, conf.model, conf.name));\n    if let Some((width, height)) = conf.physical_size {\n        if let (Ok(a), Ok(b)) = (width.try_into(), height.try_into()) {\n            new_head.physical_size(a, b);\n        }\n    }\n    let mut new_modes = Vec::with_capacity(conf.modes.len());\n    for (index, mode) in conf.modes.iter().enumerate() {\n        let new_mode = client\n            .create_resource::<ZwlrOutputModeV1, _, D>(display, new_head.version(), ())\n            .unwrap();\n        new_head.mode(&new_mode);\n        new_mode.size(i32::from(mode.width), i32::from(mode.height));\n        if mode.is_preferred {\n            new_mode.preferred();\n        }\n        if let Ok(refresh_rate) = mode.refresh_rate.try_into() {\n            new_mode.refresh(refresh_rate);\n        }\n        if Some(index) == conf.current_mode {\n            new_head.current_mode(&new_mode);\n        }\n        new_modes.push(new_mode);\n    }\n    if let Some(logical) = conf.logical {\n        new_head.position(logical.x, logical.y);\n        new_head.transform(ipc_transform_to_smithay(logical.transform).into());\n        new_head.scale(logical.scale);\n    }\n    new_head.enabled(conf.current_mode.is_some() as i32);\n    if new_head.version() >= zwlr_output_head_v1::EVT_MAKE_SINCE {\n        new_head.make(conf.make.clone());\n    }\n    if new_head.version() >= zwlr_output_head_v1::EVT_MODEL_SINCE {\n        new_head.model(conf.model.clone());\n    }\n    if new_head.version() >= zwlr_output_head_v1::EVT_SERIAL_NUMBER_SINCE {\n        if let Some(serial) = &conf.serial {\n            new_head.serial_number(serial.clone());\n        }\n    }\n\n    if new_head.version() >= zwlr_output_head_v1::EVT_ADAPTIVE_SYNC_SINCE {\n        new_head.adaptive_sync(match conf.vrr_enabled {\n            true => AdaptiveSyncState::Enabled,\n            false => AdaptiveSyncState::Disabled,\n        });\n    }\n    // new_head.serial_number(output.serial);\n    client_data.heads.insert(output, (new_head, new_modes));\n}\n"
  },
  {
    "path": "src/protocols/raw.rs",
    "content": "pub mod mutter_x11_interop {\n    pub mod v1 {\n        pub use self::generated::server;\n\n        mod generated {\n            pub mod server {\n                #![allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)]\n                #![allow(non_upper_case_globals, non_snake_case, unused_imports)]\n                #![allow(missing_docs, clippy::all)]\n\n                use smithay::reexports::wayland_server;\n                use wayland_server::protocol::*;\n\n                pub mod __interfaces {\n                    use smithay::reexports::wayland_server;\n                    use wayland_server::protocol::__interfaces::*;\n                    wayland_scanner::generate_interfaces!(\"resources/mutter-x11-interop.xml\");\n                }\n                use self::__interfaces::*;\n\n                wayland_scanner::generate_server_code!(\"resources/mutter-x11-interop.xml\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/protocols/screencopy.rs",
    "content": "use std::collections::{HashMap, HashSet};\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse calloop::generic::Generic;\nuse calloop::{Interest, LoopHandle, Mode, PostAction};\nuse smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::allocator::{Buffer, Fourcc};\nuse smithay::backend::renderer::damage::OutputDamageTracker;\nuse smithay::backend::renderer::sync::SyncPoint;\nuse smithay::output::{Output, WeakOutput};\nuse smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::{\n    zwlr_screencopy_frame_v1, zwlr_screencopy_manager_v1,\n};\nuse smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;\nuse smithay::reexports::wayland_server::protocol::wl_shm::Format;\nuse smithay::reexports::wayland_server::{\n    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,\n};\nuse smithay::utils::{Physical, Point, Rectangle, Size, Transform};\nuse smithay::wayland::{dmabuf, shm};\nuse wayland_backend::server::Credentials;\nuse zwlr_screencopy_frame_v1::{Flags, ZwlrScreencopyFrameV1};\nuse zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1;\n\nuse crate::utils::{get_credentials_for_client, get_monotonic_time, CastSessionId, CastStreamId};\n\nconst VERSION: u32 = 3;\n\n/// Inactivity timeout for considering a screencopy cast as stopped.\n///\n/// xdg-desktop-portal-wlr keeps the screencopy manager alive across casts, so there's no way to\n/// tell that a screencast had stopped. So we use a timeout: if no new with_damage frames are\n/// requested for this timeout, consider the screencast finished.\nconst CAST_TIMEOUT: Duration = Duration::from_secs(10);\n\npub struct ScreencopyQueue {\n    /// Credentials of this wlr-screencopy client, if known.\n    credentials: Option<Credentials>,\n    damage_tracker: OutputDamageTracker,\n    /// Frames waiting for the client to call copy or destroy.\n    pending_frames: HashSet<ZwlrScreencopyFrameV1>,\n    /// Queue of screencopies waiting for a corresponding output redraw with damage.\n    screencopies: Vec<Screencopy>,\n    /// Cast tracking, set when the first with_damage request arrives.\n    cast: Option<ScreencopyCast>,\n}\n\npub struct ScreencopyCast {\n    pub session_id: CastSessionId,\n    pub stream_id: CastStreamId,\n    /// Output being captured.\n    ///\n    /// Generally equal to the front entry in the queue, and persisted here when the queue becomes\n    /// empty.\n    pub output: WeakOutput,\n    /// Cached name of the output.\n    pub output_name: String,\n    /// Deadline after which this cast is considered stopped if no new frames arrive.\n    pub deadline: Duration,\n}\n\nimpl ScreencopyCast {\n    fn new(output: &Output) -> Self {\n        Self {\n            session_id: CastSessionId::next(),\n            stream_id: CastStreamId::next(),\n            output: output.downgrade(),\n            output_name: output.name(),\n            deadline: get_monotonic_time() + CAST_TIMEOUT,\n        }\n    }\n\n    fn update_deadline(&mut self) {\n        self.deadline = get_monotonic_time() + CAST_TIMEOUT;\n    }\n\n    fn update_output(&mut self, output: &Output) {\n        // Only allocate a new name when the output differs.\n        let weak = output.downgrade();\n        if self.output != weak {\n            self.output = weak;\n            self.output_name = output.name();\n        }\n    }\n}\n\nimpl ScreencopyQueue {\n    pub fn new(credentials: Option<Credentials>) -> Self {\n        Self {\n            damage_tracker: OutputDamageTracker::new((0, 0), 1.0, Transform::Normal),\n            pending_frames: HashSet::new(),\n            screencopies: Vec::new(),\n            cast: None,\n            credentials,\n        }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.pending_frames.is_empty() && self.screencopies.is_empty()\n    }\n\n    /// Get the cast tracking info, if this queue is tracking a cast.\n    pub fn cast(&self) -> Option<&ScreencopyCast> {\n        self.cast.as_ref()\n    }\n\n    pub fn credentials(&self) -> Option<Credentials> {\n        self.credentials\n    }\n\n    pub fn split(&mut self) -> (&mut OutputDamageTracker, Option<&Screencopy>) {\n        let ScreencopyQueue {\n            damage_tracker,\n            screencopies,\n            ..\n        } = self;\n        (damage_tracker, screencopies.first())\n    }\n\n    pub fn push(&mut self, screencopy: Screencopy) {\n        // Screencopy without damage is rendered immediately without the queue.\n        if !screencopy.with_damage() {\n            error!(\"only screencopy with damage can be pushed in the queue\");\n        }\n\n        if let Some(cast) = &mut self.cast {\n            // Update cast output when pushing a new front screencopy.\n            if self.screencopies.is_empty() {\n                cast.update_output(screencopy.output());\n            }\n        } else {\n            // First with_damage request, mark this as a screencast.\n            let output = screencopy.output();\n            self.cast = Some(ScreencopyCast::new(output));\n        }\n\n        self.screencopies.push(screencopy);\n    }\n\n    pub fn pop(&mut self) -> Screencopy {\n        let rv = self.screencopies.remove(0);\n\n        let cast = self.cast.as_mut().unwrap();\n        if let Some(first) = self.screencopies.first() {\n            // Update cast output (most of the time we expect this to be the same).\n            cast.update_output(first.output());\n        } else {\n            // Queue became empty, update deadline for considering the cast stopped.\n            cast.update_deadline();\n        }\n\n        rv\n    }\n\n    pub fn clear_expired_cast(&mut self) {\n        if let Some(cast) = &self.cast {\n            // Check deadline if there are no in-flight frames.\n            if self.screencopies.is_empty() && cast.deadline <= get_monotonic_time() {\n                self.cast = None;\n            }\n        }\n    }\n\n    fn remove_output(&mut self, output: &Output) {\n        if self.screencopies.is_empty() {\n            return;\n        }\n\n        self.screencopies\n            .retain(|screencopy| screencopy.output() != output);\n\n        if let Some(cast) = &mut self.cast {\n            if self.screencopies.is_empty() {\n                // Queue became empty, update deadline for considering the cast stopped.\n                cast.update_deadline();\n            }\n        }\n    }\n\n    fn remove_frame(&mut self, frame: &ZwlrScreencopyFrameV1) {\n        self.pending_frames.remove(frame);\n\n        if self.screencopies.is_empty() {\n            return;\n        }\n\n        self.screencopies\n            .retain(|screencopy| screencopy.frame != *frame);\n\n        if let Some(cast) = &mut self.cast {\n            if self.screencopies.is_empty() {\n                // Queue became empty, update deadline for considering the cast stopped.\n                cast.update_deadline();\n            }\n        }\n    }\n}\n\n#[derive(Default)]\npub struct ScreencopyManagerState {\n    queues: HashMap<ZwlrScreencopyManagerV1, ScreencopyQueue>,\n}\n\npub struct ScreencopyManagerGlobalData {\n    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,\n}\n\nimpl ScreencopyManagerState {\n    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self\n    where\n        D: GlobalDispatch<ZwlrScreencopyManagerV1, ScreencopyManagerGlobalData>,\n        D: Dispatch<ZwlrScreencopyManagerV1, ()>,\n        D: Dispatch<ZwlrScreencopyFrameV1, ScreencopyFrameState>,\n        D: ScreencopyHandler,\n        D: 'static,\n        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,\n    {\n        let global_data = ScreencopyManagerGlobalData {\n            filter: Box::new(filter),\n        };\n        display.create_global::<D, ZwlrScreencopyManagerV1, _>(VERSION, global_data);\n\n        Self {\n            queues: HashMap::new(),\n        }\n    }\n\n    pub fn push(&mut self, manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy) {\n        let Some(queue) = self.queues.get_mut(manager) else {\n            // Destroying the manager does not invalidate existing frames, so the queue should\n            // keep existing.\n            error!(\"screencopy queue must not be deleted as long as frames exist\");\n            return;\n        };\n\n        queue.push(screencopy);\n    }\n\n    pub fn damage_tracker(\n        &mut self,\n        manager: &ZwlrScreencopyManagerV1,\n    ) -> Option<&mut OutputDamageTracker> {\n        let queue = self.queues.get_mut(manager)?;\n        Some(&mut queue.damage_tracker)\n    }\n\n    pub fn remove_output(&mut self, output: &Output) {\n        for queue in self.queues.values_mut() {\n            queue.remove_output(output);\n        }\n\n        self.cleanup_queues();\n    }\n\n    pub fn queues(&self) -> impl Iterator<Item = &ScreencopyQueue> {\n        self.queues.values()\n    }\n\n    pub fn with_queues_mut(&mut self, mut f: impl FnMut(&mut ScreencopyQueue)) {\n        for queue in self.queues.values_mut() {\n            f(queue);\n        }\n\n        self.cleanup_queues();\n    }\n\n    fn cleanup_queues(&mut self) {\n        self.queues\n            .retain(|manager, queue| manager.is_alive() || !queue.is_empty());\n    }\n\n    pub fn clear_expired_casts(&mut self) {\n        for queue in self.queues.values_mut() {\n            queue.clear_expired_cast();\n        }\n    }\n}\n\nimpl<D> GlobalDispatch<ZwlrScreencopyManagerV1, ScreencopyManagerGlobalData, D>\n    for ScreencopyManagerState\nwhere\n    D: GlobalDispatch<ZwlrScreencopyManagerV1, ScreencopyManagerGlobalData>,\n    D: Dispatch<ZwlrScreencopyManagerV1, ()>,\n    D: Dispatch<ZwlrScreencopyFrameV1, ScreencopyFrameState>,\n    D: ScreencopyHandler,\n    D: 'static,\n{\n    fn bind(\n        state: &mut D,\n        dh: &DisplayHandle,\n        client: &Client,\n        manager: New<ZwlrScreencopyManagerV1>,\n        _manager_state: &ScreencopyManagerGlobalData,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        let manager = data_init.init(manager, ());\n\n        let state = state.screencopy_state();\n        let credentials = get_credentials_for_client(dh, client);\n        let queue = ScreencopyQueue::new(credentials);\n        state.queues.insert(manager.clone(), queue);\n    }\n\n    fn can_view(client: Client, global_data: &ScreencopyManagerGlobalData) -> bool {\n        (global_data.filter)(&client)\n    }\n}\n\nimpl<D> Dispatch<ZwlrScreencopyManagerV1, (), D> for ScreencopyManagerState\nwhere\n    D: GlobalDispatch<ZwlrScreencopyManagerV1, ScreencopyManagerGlobalData>,\n    D: Dispatch<ZwlrScreencopyManagerV1, ()>,\n    D: Dispatch<ZwlrScreencopyFrameV1, ScreencopyFrameState>,\n    D: ScreencopyHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        manager: &ZwlrScreencopyManagerV1,\n        request: zwlr_screencopy_manager_v1::Request,\n        _data: &(),\n        _display: &DisplayHandle,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        let (frame, overlay_cursor, buffer_size, region_loc, output) = match request {\n            zwlr_screencopy_manager_v1::Request::CaptureOutput {\n                frame,\n                overlay_cursor,\n                output,\n            } => {\n                let Some(output) = Output::from_resource(&output) else {\n                    trace!(\"screencopy client requested non-existent output\");\n                    let frame = data_init.init(frame, ScreencopyFrameState::Failed);\n                    frame.failed();\n                    return;\n                };\n\n                let buffer_size = output.current_mode().unwrap().size;\n                let region_loc = Point::from((0, 0));\n\n                (frame, overlay_cursor, buffer_size, region_loc, output)\n            }\n            zwlr_screencopy_manager_v1::Request::CaptureOutputRegion {\n                frame,\n                overlay_cursor,\n                x,\n                y,\n                width,\n                height,\n                output,\n            } => {\n                if width <= 0 || height <= 0 {\n                    trace!(\"screencopy client requested invalid sized region\");\n                    let frame = data_init.init(frame, ScreencopyFrameState::Failed);\n                    frame.failed();\n                    return;\n                }\n\n                let Some(output) = Output::from_resource(&output) else {\n                    trace!(\"screencopy client requested non-existent output\");\n                    let frame = data_init.init(frame, ScreencopyFrameState::Failed);\n                    frame.failed();\n                    return;\n                };\n\n                let output_transform = output.current_transform();\n                let output_physical_size =\n                    output_transform.transform_size(output.current_mode().unwrap().size);\n                let output_rect = Rectangle::from_size(output_physical_size);\n\n                let rect = Rectangle::new(Point::from((x, y)), Size::from((width, height)));\n\n                let output_scale = output.current_scale().fractional_scale();\n                let physical_rect = rect.to_physical_precise_round(output_scale);\n\n                // Clamp captured region to the output.\n                let Some(clamped_rect) = physical_rect.intersection(output_rect) else {\n                    trace!(\"screencopy client requested region outside of output\");\n                    let frame = data_init.init(frame, ScreencopyFrameState::Failed);\n                    frame.failed();\n                    return;\n                };\n\n                let untransformed_rect = output_transform\n                    .invert()\n                    .transform_rect_in(clamped_rect, &output_physical_size);\n\n                (\n                    frame,\n                    overlay_cursor,\n                    untransformed_rect.size,\n                    clamped_rect.loc,\n                    output,\n                )\n            }\n            zwlr_screencopy_manager_v1::Request::Destroy => return,\n            _ => unreachable!(),\n        };\n\n        // Create the frame.\n        let overlay_cursor = overlay_cursor != 0;\n        let info = ScreencopyFrameInfo {\n            output,\n            overlay_cursor,\n            buffer_size,\n            region_loc,\n        };\n        let frame = data_init.init(\n            frame,\n            ScreencopyFrameState::Pending {\n                manager: manager.clone(),\n                info,\n                copied: Arc::new(AtomicBool::new(false)),\n            },\n        );\n\n        // Send desired SHM buffer parameters.\n        frame.buffer(\n            Format::Xrgb8888,\n            buffer_size.w as u32,\n            buffer_size.h as u32,\n            buffer_size.w as u32 * 4,\n        );\n\n        if frame.version() >= 3 {\n            // Send desired DMA buffer parameters.\n            frame.linux_dmabuf(\n                Fourcc::Xrgb8888 as u32,\n                buffer_size.w as u32,\n                buffer_size.h as u32,\n            );\n\n            // Notify client that all supported buffers were enumerated.\n            frame.buffer_done();\n        }\n\n        let state = state.screencopy_state();\n        let queue = state.queues.get_mut(manager).unwrap();\n        queue.pending_frames.insert(frame);\n    }\n\n    fn destroyed(\n        state: &mut D,\n        _client: wayland_backend::server::ClientId,\n        manager: &ZwlrScreencopyManagerV1,\n        _data: &(),\n    ) {\n        let state = state.screencopy_state();\n\n        let Some(queue) = state.queues.get_mut(manager) else {\n            // This happened once. I'm really not sure how exactly though.\n            //\n            // I've dug into wayland-server and wayland-backend, and apparently there are a bunch\n            // of places where calling destroyed() is delayed (even on a +1 ms timer). Then, it's\n            // quite possible for some code to run cleanup_queues() *before* this destroyed()\n            // handler, and delete the queue because the manager is no longer .is_alive() by then.\n            // Then, queue will be None here.\n            //\n            // My attempts to reproduce this in a test have failed though. Perhaps it requires a\n            // tricky timing condition where the client disconnects at some precise spot inside our\n            // State::refresh_and_flush_clients() call.\n            return;\n        };\n\n        // Clean up the queue if this was the last object.\n        if queue.is_empty() {\n            state.queues.remove(manager);\n        }\n    }\n}\n\n/// Handler trait for wlr-screencopy.\npub trait ScreencopyHandler {\n    /// Handle new screencopy request.\n    ///\n    /// The handler must synchronously either ready/fail the screencopy, or submit it to the\n    /// manager queue.\n    fn frame(&mut self, manager: &ZwlrScreencopyManagerV1, screencopy: Screencopy);\n\n    fn screencopy_state(&mut self) -> &mut ScreencopyManagerState;\n}\n\n#[allow(missing_docs)]\n#[macro_export]\nmacro_rules! delegate_screencopy {\n    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {\n        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1: $crate::protocols::screencopy::ScreencopyManagerGlobalData\n        ] => $crate::protocols::screencopy::ScreencopyManagerState);\n\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1: ()\n        ] => $crate::protocols::screencopy::ScreencopyManagerState);\n\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::screencopy::v1::server::zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1: $crate::protocols::screencopy::ScreencopyFrameState\n        ] => $crate::protocols::screencopy::ScreencopyManagerState);\n    };\n}\n\n#[derive(Clone)]\npub struct ScreencopyFrameInfo {\n    output: Output,\n    buffer_size: Size<i32, Physical>,\n    region_loc: Point<i32, Physical>,\n    overlay_cursor: bool,\n}\n\npub enum ScreencopyFrameState {\n    Failed,\n    Pending {\n        manager: ZwlrScreencopyManagerV1,\n        info: ScreencopyFrameInfo,\n        copied: Arc<AtomicBool>,\n    },\n}\n\nimpl<D> Dispatch<ZwlrScreencopyFrameV1, ScreencopyFrameState, D> for ScreencopyManagerState\nwhere\n    D: Dispatch<ZwlrScreencopyFrameV1, ScreencopyFrameState>,\n    D: ScreencopyHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        frame: &ZwlrScreencopyFrameV1,\n        request: zwlr_screencopy_frame_v1::Request,\n        data: &ScreencopyFrameState,\n        _display: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        if matches!(request, zwlr_screencopy_frame_v1::Request::Destroy) {\n            return;\n        }\n\n        let ScreencopyFrameState::Pending {\n            manager,\n            info,\n            copied,\n        } = data\n        else {\n            return;\n        };\n\n        if copied.load(Ordering::SeqCst) {\n            frame.post_error(\n                zwlr_screencopy_frame_v1::Error::AlreadyUsed,\n                \"copy was already requested\",\n            );\n            return;\n        }\n\n        let (buffer, with_damage) = match request {\n            zwlr_screencopy_frame_v1::Request::Copy { buffer } => (buffer, false),\n            zwlr_screencopy_frame_v1::Request::CopyWithDamage { buffer } => (buffer, true),\n            _ => unreachable!(),\n        };\n\n        let size = info.buffer_size;\n\n        let buffer = if let Ok(dmabuf) = dmabuf::get_dmabuf(&buffer) {\n            if dmabuf.format().code == Fourcc::Xrgb8888\n                && dmabuf.width() == size.w as u32\n                && dmabuf.height() == size.h as u32\n            {\n                ScreencopyBuffer::Dmabuf(dmabuf.clone())\n            } else {\n                frame.post_error(\n                    zwlr_screencopy_frame_v1::Error::InvalidBuffer,\n                    \"invalid dmabuf parameters\",\n                );\n                return;\n            }\n        } else if shm::with_buffer_contents(&buffer, |_, shm_len, buffer_data| {\n            buffer_data.format == Format::Xrgb8888\n                && buffer_data.width == size.w\n                && buffer_data.height == size.h\n                && buffer_data.stride == size.w * 4\n                && shm_len == buffer_data.stride as usize * buffer_data.height as usize\n        })\n        .unwrap_or(false)\n        {\n            ScreencopyBuffer::Shm(buffer)\n        } else {\n            frame.post_error(\n                zwlr_screencopy_frame_v1::Error::InvalidBuffer,\n                \"invalid buffer\",\n            );\n            return;\n        };\n\n        copied.store(true, Ordering::SeqCst);\n\n        state.frame(\n            manager,\n            Screencopy {\n                buffer,\n                frame: frame.clone(),\n                info: info.clone(),\n                with_damage,\n                submitted: false,\n            },\n        );\n\n        // By this point the frame should've been either copied or failed or pushed to the queue,\n        // so remove it from pending frames.\n        let state = state.screencopy_state();\n        let queue = state.queues.get_mut(manager).unwrap();\n        queue.pending_frames.remove(frame);\n        if queue.is_empty() && !manager.is_alive() {\n            state.queues.remove(manager);\n        }\n    }\n\n    fn destroyed(\n        state: &mut D,\n        _client: wayland_backend::server::ClientId,\n        frame: &ZwlrScreencopyFrameV1,\n        data: &ScreencopyFrameState,\n    ) {\n        let ScreencopyFrameState::Pending { manager, .. } = data else {\n            return;\n        };\n\n        let state = state.screencopy_state();\n        let Some(queue) = state.queues.get_mut(manager) else {\n            // I think this can happen when we post_error() on a pending frame? Either way better\n            // safe than sorry.\n            return;\n        };\n\n        queue.remove_frame(frame);\n\n        // Clean up the queue if this was the last object.\n        if queue.is_empty() && !manager.is_alive() {\n            state.queues.remove(manager);\n        }\n    }\n}\n\n/// Screencopy buffer.\n#[derive(Clone)]\npub enum ScreencopyBuffer {\n    Dmabuf(Dmabuf),\n    Shm(WlBuffer),\n}\n\n/// Screencopy frame.\npub struct Screencopy {\n    info: ScreencopyFrameInfo,\n    frame: ZwlrScreencopyFrameV1,\n    buffer: ScreencopyBuffer,\n    with_damage: bool,\n    submitted: bool,\n}\n\nimpl Drop for Screencopy {\n    fn drop(&mut self) {\n        if !self.submitted {\n            self.frame.failed();\n        }\n    }\n}\n\nimpl Screencopy {\n    /// Get the target buffer to copy to.\n    pub fn buffer(&self) -> &ScreencopyBuffer {\n        &self.buffer\n    }\n\n    pub fn region_loc(&self) -> Point<i32, Physical> {\n        self.info.region_loc\n    }\n\n    pub fn buffer_size(&self) -> Size<i32, Physical> {\n        self.info.buffer_size\n    }\n\n    pub fn output(&self) -> &Output {\n        &self.info.output\n    }\n\n    pub fn overlay_cursor(&self) -> bool {\n        self.info.overlay_cursor\n    }\n\n    pub fn with_damage(&self) -> bool {\n        self.with_damage\n    }\n\n    pub fn damage(&self, damages: impl Iterator<Item = Rectangle<i32, smithay::utils::Buffer>>) {\n        for Rectangle { loc, size } in damages {\n            self.frame\n                .damage(loc.x as u32, loc.y as u32, size.w as u32, size.h as u32);\n        }\n    }\n\n    /// Submit the copied content.\n    fn submit(mut self, y_invert: bool, timestamp: Duration) {\n        // Notify client that buffer is ordinary.\n        self.frame.flags(if y_invert {\n            Flags::YInvert\n        } else {\n            Flags::empty()\n        });\n\n        // Notify client about successful copy.\n        let tv_sec_hi = (timestamp.as_secs() >> 32) as u32;\n        let tv_sec_lo = (timestamp.as_secs() & 0xFFFFFFFF) as u32;\n        let tv_nsec = timestamp.subsec_nanos();\n        self.frame.ready(tv_sec_hi, tv_sec_lo, tv_nsec);\n\n        // Mark frame as submitted to ensure destructor isn't run.\n        self.submitted = true;\n    }\n\n    pub fn submit_after_sync<T>(\n        self,\n        y_invert: bool,\n        sync_point: Option<SyncPoint>,\n        event_loop: &LoopHandle<'_, T>,\n    ) {\n        let timestamp = get_monotonic_time();\n        match sync_point.and_then(|s| s.export()) {\n            None => self.submit(y_invert, timestamp),\n            Some(sync_fd) => {\n                let source = Generic::new(sync_fd, Interest::READ, Mode::OneShot);\n                let mut screencopy = Some(self);\n                event_loop\n                    .insert_source(source, move |_, _, _| {\n                        screencopy.take().unwrap().submit(y_invert, timestamp);\n                        Ok(PostAction::Remove)\n                    })\n                    .unwrap();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/protocols/virtual_pointer.rs",
    "content": "use std::collections::HashSet;\nuse std::sync::Mutex;\n\nuse smithay::backend::input::{\n    AbsolutePositionEvent, Axis, AxisRelativeDirection, AxisSource, ButtonState, Device,\n    DeviceCapability, Event, InputBackend, PointerAxisEvent, PointerButtonEvent,\n    PointerMotionAbsoluteEvent, PointerMotionEvent, UnusedEvent,\n};\nuse smithay::input::pointer::AxisFrame;\nuse smithay::output::Output;\nuse smithay::reexports::wayland_protocols_wlr;\nuse smithay::reexports::wayland_server::protocol::wl_pointer;\nuse smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;\nuse smithay::reexports::wayland_server::{\n    Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,\n};\nuse wayland_backend::protocol::WEnum;\nuse wayland_protocols_wlr::virtual_pointer::v1::server::{\n    zwlr_virtual_pointer_manager_v1, zwlr_virtual_pointer_v1,\n};\nuse zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1;\nuse zwlr_virtual_pointer_v1::ZwlrVirtualPointerV1;\n\nconst VERSION: u32 = 2;\n\npub struct VirtualPointerManagerState {\n    virtual_pointers: HashSet<ZwlrVirtualPointerV1>,\n}\n\npub struct VirtualPointerManagerGlobalData {\n    filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,\n}\n\npub struct VirtualPointerInputBackend;\n\n#[derive(Clone, Debug, Hash, Eq, PartialEq)]\npub struct VirtualPointer {\n    pointer: ZwlrVirtualPointerV1,\n}\n\n#[derive(Debug)]\npub struct VirtualPointerUserData {\n    seat: Option<WlSeat>,\n    output: Option<Output>,\n\n    axis_frame: Mutex<Option<AxisFrame>>,\n}\n\nimpl VirtualPointer {\n    fn data(&self) -> &VirtualPointerUserData {\n        self.pointer.data().unwrap()\n    }\n\n    pub fn seat(&self) -> Option<&WlSeat> {\n        self.data().seat.as_ref()\n    }\n\n    pub fn output(&self) -> Option<&Output> {\n        self.data().output.as_ref()\n    }\n\n    fn finish_axis_frame(&self) -> Option<AxisFrame> {\n        self.data().axis_frame.lock().unwrap().take()\n    }\n\n    fn mutate_axis_frame(&self, time: Option<u32>, f: impl FnOnce(AxisFrame) -> AxisFrame) {\n        let mut frame = self.data().axis_frame.lock().unwrap();\n\n        *frame = frame.or(time.map(AxisFrame::new)).map(f);\n    }\n}\n\nimpl Device for VirtualPointer {\n    fn id(&self) -> String {\n        format!(\"wlr virtual pointer {}\", self.pointer.id())\n    }\n\n    fn name(&self) -> String {\n        String::from(\"virtual pointer\")\n    }\n\n    fn has_capability(&self, capability: DeviceCapability) -> bool {\n        matches!(capability, DeviceCapability::Pointer)\n    }\n\n    fn usb_id(&self) -> Option<(u32, u32)> {\n        None\n    }\n\n    fn syspath(&self) -> Option<std::path::PathBuf> {\n        None\n    }\n}\n\npub struct VirtualPointerMotionEvent {\n    pointer: VirtualPointer,\n    time: u32,\n    dx: f64,\n    dy: f64,\n}\n\nimpl Event<VirtualPointerInputBackend> for VirtualPointerMotionEvent {\n    fn time(&self) -> u64 {\n        self.time as u64 * 1000 // millis to micros\n    }\n\n    fn device(&self) -> VirtualPointer {\n        self.pointer.clone()\n    }\n}\n\nimpl PointerMotionEvent<VirtualPointerInputBackend> for VirtualPointerMotionEvent {\n    fn delta_x(&self) -> f64 {\n        self.dx\n    }\n\n    fn delta_y(&self) -> f64 {\n        self.dy\n    }\n\n    fn delta_x_unaccel(&self) -> f64 {\n        self.dx\n    }\n\n    fn delta_y_unaccel(&self) -> f64 {\n        self.dy\n    }\n}\n\npub struct VirtualPointerMotionAbsoluteEvent {\n    pointer: VirtualPointer,\n    time: u32,\n    x: u32,\n    y: u32,\n    x_extent: u32,\n    y_extent: u32,\n}\n\nimpl Event<VirtualPointerInputBackend> for VirtualPointerMotionAbsoluteEvent {\n    fn time(&self) -> u64 {\n        self.time as u64 * 1000 // millis to micros\n    }\n\n    fn device(&self) -> VirtualPointer {\n        self.pointer.clone()\n    }\n}\n\nimpl AbsolutePositionEvent<VirtualPointerInputBackend> for VirtualPointerMotionAbsoluteEvent {\n    fn x(&self) -> f64 {\n        self.x as f64 / self.x_extent as f64\n    }\n\n    fn y(&self) -> f64 {\n        self.y as f64 / self.y_extent as f64\n    }\n\n    fn x_transformed(&self, width: i32) -> f64 {\n        (self.x as i64 * width as i64) as f64 / self.x_extent as f64\n    }\n\n    fn y_transformed(&self, height: i32) -> f64 {\n        (self.y as i64 * height as i64) as f64 / self.y_extent as f64\n    }\n}\n\npub struct VirtualPointerButtonEvent {\n    pointer: VirtualPointer,\n    time: u32,\n    button: u32,\n    state: ButtonState,\n}\n\nimpl Event<VirtualPointerInputBackend> for VirtualPointerButtonEvent {\n    fn time(&self) -> u64 {\n        self.time as u64 * 1000 // millis to micros\n    }\n\n    fn device(&self) -> VirtualPointer {\n        self.pointer.clone()\n    }\n}\n\nimpl PointerButtonEvent<VirtualPointerInputBackend> for VirtualPointerButtonEvent {\n    fn button_code(&self) -> u32 {\n        self.button\n    }\n\n    fn state(&self) -> ButtonState {\n        self.state\n    }\n}\n\npub struct VirtualPointerAxisEvent {\n    pointer: VirtualPointer,\n    frame: AxisFrame,\n}\n\nimpl Event<VirtualPointerInputBackend> for VirtualPointerAxisEvent {\n    fn time(&self) -> u64 {\n        self.frame.time as u64 * 1000 // millis to micros\n    }\n\n    fn device(&self) -> VirtualPointer {\n        self.pointer.clone()\n    }\n}\n\nfn tuple_axis<T>(tuple: (T, T), axis: Axis) -> T {\n    match axis {\n        Axis::Horizontal => tuple.0,\n        Axis::Vertical => tuple.1,\n    }\n}\n\nimpl PointerAxisEvent<VirtualPointerInputBackend> for VirtualPointerAxisEvent {\n    fn amount(&self, axis: Axis) -> Option<f64> {\n        Some(tuple_axis(self.frame.axis, axis))\n    }\n\n    fn amount_v120(&self, axis: Axis) -> Option<f64> {\n        self.frame.v120.map(|v120| tuple_axis(v120, axis) as f64)\n    }\n\n    fn source(&self) -> AxisSource {\n        self.frame.source.unwrap_or_else(|| {\n            warn!(\"AxisSource: no source set, giving bogus value\");\n            AxisSource::Continuous\n        })\n    }\n\n    fn relative_direction(&self, axis: Axis) -> AxisRelativeDirection {\n        tuple_axis(self.frame.relative_direction, axis)\n    }\n}\n\nimpl PointerMotionAbsoluteEvent<VirtualPointerInputBackend> for VirtualPointerMotionAbsoluteEvent {}\n\nimpl InputBackend for VirtualPointerInputBackend {\n    type Device = VirtualPointer;\n\n    type KeyboardKeyEvent = UnusedEvent;\n    type PointerAxisEvent = VirtualPointerAxisEvent;\n    type PointerButtonEvent = VirtualPointerButtonEvent;\n    type PointerMotionEvent = VirtualPointerMotionEvent;\n    type PointerMotionAbsoluteEvent = VirtualPointerMotionAbsoluteEvent;\n\n    type GestureSwipeBeginEvent = UnusedEvent;\n    type GestureSwipeUpdateEvent = UnusedEvent;\n    type GestureSwipeEndEvent = UnusedEvent;\n    type GesturePinchBeginEvent = UnusedEvent;\n    type GesturePinchUpdateEvent = UnusedEvent;\n    type GesturePinchEndEvent = UnusedEvent;\n    type GestureHoldBeginEvent = UnusedEvent;\n    type GestureHoldEndEvent = UnusedEvent;\n\n    type TouchDownEvent = UnusedEvent;\n    type TouchUpEvent = UnusedEvent;\n    type TouchMotionEvent = UnusedEvent;\n    type TouchCancelEvent = UnusedEvent;\n    type TouchFrameEvent = UnusedEvent;\n    type TabletToolAxisEvent = UnusedEvent;\n    type TabletToolProximityEvent = UnusedEvent;\n    type TabletToolTipEvent = UnusedEvent;\n    type TabletToolButtonEvent = UnusedEvent;\n\n    type SwitchToggleEvent = UnusedEvent;\n\n    type SpecialEvent = UnusedEvent;\n}\n\npub trait VirtualPointerHandler {\n    fn virtual_pointer_manager_state(&mut self) -> &mut VirtualPointerManagerState;\n\n    fn create_virtual_pointer(&mut self, pointer: VirtualPointer) {\n        let _ = pointer;\n    }\n    fn destroy_virtual_pointer(&mut self, pointer: VirtualPointer) {\n        let _ = pointer;\n    }\n\n    fn on_virtual_pointer_motion(&mut self, event: VirtualPointerMotionEvent);\n    fn on_virtual_pointer_motion_absolute(&mut self, event: VirtualPointerMotionAbsoluteEvent);\n    fn on_virtual_pointer_button(&mut self, event: VirtualPointerButtonEvent);\n    fn on_virtual_pointer_axis(&mut self, event: VirtualPointerAxisEvent);\n}\n\nimpl VirtualPointerManagerState {\n    pub fn new<D, F>(display: &DisplayHandle, filter: F) -> Self\n    where\n        D: GlobalDispatch<ZwlrVirtualPointerManagerV1, VirtualPointerManagerGlobalData>,\n        D: Dispatch<ZwlrVirtualPointerManagerV1, ()>,\n        D: Dispatch<ZwlrVirtualPointerV1, VirtualPointerUserData>,\n        D: VirtualPointerHandler,\n        D: 'static,\n        F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,\n    {\n        let global_data = VirtualPointerManagerGlobalData {\n            filter: Box::new(filter),\n        };\n        display.create_global::<D, ZwlrVirtualPointerManagerV1, _>(VERSION, global_data);\n\n        Self {\n            virtual_pointers: HashSet::new(),\n        }\n    }\n}\n\nimpl<D> GlobalDispatch<ZwlrVirtualPointerManagerV1, VirtualPointerManagerGlobalData, D>\n    for VirtualPointerManagerState\nwhere\n    D: GlobalDispatch<ZwlrVirtualPointerManagerV1, VirtualPointerManagerGlobalData>,\n    D: Dispatch<ZwlrVirtualPointerManagerV1, ()>,\n    D: Dispatch<ZwlrVirtualPointerV1, VirtualPointerUserData>,\n    D: VirtualPointerHandler,\n    D: 'static,\n{\n    fn bind(\n        _state: &mut D,\n        _handle: &DisplayHandle,\n        _client: &Client,\n        manager: New<ZwlrVirtualPointerManagerV1>,\n        _manager_state: &VirtualPointerManagerGlobalData,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        data_init.init(manager, ());\n    }\n\n    fn can_view(client: Client, global_data: &VirtualPointerManagerGlobalData) -> bool {\n        (global_data.filter)(&client)\n    }\n}\n\nimpl<D> Dispatch<ZwlrVirtualPointerManagerV1, (), D> for VirtualPointerManagerState\nwhere\n    D: Dispatch<ZwlrVirtualPointerManagerV1, ()>,\n    D: Dispatch<ZwlrVirtualPointerV1, VirtualPointerUserData>,\n    D: VirtualPointerHandler,\n    D: 'static,\n{\n    fn request(\n        state: &mut D,\n        _client: &Client,\n        _resource: &ZwlrVirtualPointerManagerV1,\n        request: <ZwlrVirtualPointerManagerV1 as Resource>::Request,\n        _data: &(),\n        _dhandle: &DisplayHandle,\n        data_init: &mut DataInit<'_, D>,\n    ) {\n        let (id, seat, output) = match request {\n            zwlr_virtual_pointer_manager_v1::Request::CreateVirtualPointer { seat, id } => {\n                (id, seat, None)\n            }\n            zwlr_virtual_pointer_manager_v1::Request::CreateVirtualPointerWithOutput {\n                seat,\n                output,\n                id,\n            } => (id, seat, output.as_ref().and_then(Output::from_resource)),\n            zwlr_virtual_pointer_manager_v1::Request::Destroy => return,\n            _ => unreachable!(),\n        };\n\n        let pointer = data_init.init(\n            id,\n            VirtualPointerUserData {\n                seat,\n                output,\n                axis_frame: Mutex::new(None),\n            },\n        );\n        state\n            .virtual_pointer_manager_state()\n            .virtual_pointers\n            .insert(pointer.clone());\n\n        state.create_virtual_pointer(VirtualPointer { pointer });\n    }\n}\n\nimpl<D> Dispatch<ZwlrVirtualPointerV1, VirtualPointerUserData, D> for VirtualPointerManagerState\nwhere\n    D: Dispatch<ZwlrVirtualPointerV1, VirtualPointerUserData>,\n    D: VirtualPointerHandler,\n    D: 'static,\n{\n    fn request(\n        handler: &mut D,\n        _client: &Client,\n        resource: &ZwlrVirtualPointerV1,\n        request: <ZwlrVirtualPointerV1 as Resource>::Request,\n        _data: &VirtualPointerUserData,\n        _dhandle: &DisplayHandle,\n        _data_init: &mut DataInit<'_, D>,\n    ) {\n        let pointer = VirtualPointer {\n            pointer: resource.clone(),\n        };\n        match request {\n            zwlr_virtual_pointer_v1::Request::Motion { time, dx, dy } => {\n                let event = VirtualPointerMotionEvent {\n                    pointer,\n                    time,\n                    dx,\n                    dy,\n                };\n                handler.on_virtual_pointer_motion(event);\n            }\n            zwlr_virtual_pointer_v1::Request::MotionAbsolute {\n                time,\n                x,\n                y,\n                x_extent,\n                y_extent,\n            } => {\n                let event = VirtualPointerMotionAbsoluteEvent {\n                    pointer,\n                    time,\n                    x,\n                    y,\n                    x_extent,\n                    y_extent,\n                };\n                handler.on_virtual_pointer_motion_absolute(event);\n            }\n            zwlr_virtual_pointer_v1::Request::Button {\n                time,\n                button,\n                state,\n            } => {\n                // state is an enum but wlroots treats it as a C boolean (zero or nonzero)\n                // so we emulate that behaviour too. ButtonState::Pressed and any invalid value\n                // counts as pressed.\n                // https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/3187479c07c34a4de82c06a316a763a36a0499da/types/wlr_virtual_pointer_v1.c#L74\n                let state = match state {\n                    WEnum::Value(wl_pointer::ButtonState::Released) => ButtonState::Released,\n                    _ => ButtonState::Pressed,\n                };\n                let event = VirtualPointerButtonEvent {\n                    pointer,\n                    time,\n                    button,\n                    state,\n                };\n                handler.on_virtual_pointer_button(event);\n            }\n            zwlr_virtual_pointer_v1::Request::Axis { time, axis, value } => {\n                let axis = match axis {\n                    WEnum::Value(wl_pointer::Axis::VerticalScroll) => Axis::Vertical,\n                    WEnum::Value(wl_pointer::Axis::HorizontalScroll) => Axis::Horizontal,\n                    _ => {\n                        warn!(\"Axis: invalid axis\");\n                        resource.post_error(\n                            zwlr_virtual_pointer_v1::Error::InvalidAxis,\n                            \"invalid axis\",\n                        );\n                        return;\n                    }\n                };\n\n                pointer.mutate_axis_frame(Some(time), |frame| frame.value(axis, value));\n            }\n            zwlr_virtual_pointer_v1::Request::Frame => {\n                if let Some(frame) = pointer.finish_axis_frame() {\n                    let event = VirtualPointerAxisEvent { pointer, frame };\n                    handler.on_virtual_pointer_axis(event);\n                }\n            }\n            zwlr_virtual_pointer_v1::Request::AxisSource { axis_source } => {\n                let axis_source = match axis_source {\n                    WEnum::Value(wl_pointer::AxisSource::Wheel) => AxisSource::Wheel,\n                    WEnum::Value(wl_pointer::AxisSource::Finger) => AxisSource::Finger,\n                    WEnum::Value(wl_pointer::AxisSource::Continuous) => AxisSource::Continuous,\n                    WEnum::Value(wl_pointer::AxisSource::WheelTilt) => AxisSource::WheelTilt,\n\n                    _ => {\n                        warn!(\"AxisSource: invalid axis source\");\n                        resource.post_error(\n                            zwlr_virtual_pointer_v1::Error::InvalidAxisSource,\n                            \"invalid axis source\",\n                        );\n                        return;\n                    }\n                };\n\n                pointer.mutate_axis_frame(None, |frame| frame.source(axis_source));\n            }\n            zwlr_virtual_pointer_v1::Request::AxisStop { time, axis } => {\n                let axis = match axis {\n                    WEnum::Value(wl_pointer::Axis::VerticalScroll) => Axis::Vertical,\n                    WEnum::Value(wl_pointer::Axis::HorizontalScroll) => Axis::Horizontal,\n                    _ => {\n                        warn!(\"AxisStop: invalid axis\");\n                        resource.post_error(\n                            zwlr_virtual_pointer_v1::Error::InvalidAxis,\n                            \"invalid axis\",\n                        );\n                        return;\n                    }\n                };\n\n                pointer.mutate_axis_frame(Some(time), |frame| frame.stop(axis));\n            }\n            zwlr_virtual_pointer_v1::Request::AxisDiscrete {\n                time,\n                axis,\n                value,\n                discrete,\n            } => {\n                let axis = match axis {\n                    WEnum::Value(wl_pointer::Axis::VerticalScroll) => Axis::Vertical,\n                    WEnum::Value(wl_pointer::Axis::HorizontalScroll) => Axis::Horizontal,\n                    _ => {\n                        warn!(\"AxisDiscrete: invalid axis\");\n                        resource.post_error(\n                            zwlr_virtual_pointer_v1::Error::InvalidAxis,\n                            \"invalid axis\",\n                        );\n                        return;\n                    }\n                };\n                pointer.mutate_axis_frame(Some(time), |frame| {\n                    frame.value(axis, value).v120(axis, discrete * 120)\n                });\n            }\n            zwlr_virtual_pointer_v1::Request::Destroy => {}\n            _ => unreachable!(),\n        }\n    }\n\n    fn destroyed(\n        handler: &mut D,\n        _client: wayland_backend::server::ClientId,\n        resource: &ZwlrVirtualPointerV1,\n        _data: &VirtualPointerUserData,\n    ) {\n        let pointer = VirtualPointer {\n            pointer: resource.clone(),\n        };\n\n        handler.destroy_virtual_pointer(pointer);\n        handler\n            .virtual_pointer_manager_state()\n            .virtual_pointers\n            .remove(resource);\n    }\n}\n\n#[macro_export]\nmacro_rules! delegate_virtual_pointer {\n    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {\n        smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::virtual_pointer::v1::server::zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1: $crate::protocols::virtual_pointer::VirtualPointerManagerGlobalData\n            ] => $crate::protocols::virtual_pointer::VirtualPointerManagerState);\n\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::virtual_pointer::v1::server::zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1: ()\n        ] => $crate::protocols::virtual_pointer::VirtualPointerManagerState);\n\n        smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [\n            smithay::reexports::wayland_protocols_wlr::virtual_pointer::v1::server::zwlr_virtual_pointer_v1::ZwlrVirtualPointerV1:  $crate::protocols::virtual_pointer::VirtualPointerUserData\n        ] => $crate::protocols::virtual_pointer::VirtualPointerManagerState);\n    };\n}\n"
  },
  {
    "path": "src/render_helpers/border.rs",
    "content": "use std::collections::HashMap;\nuse std::rc::Rc;\n\nuse glam::{Mat3, Vec2};\nuse niri_config::{\n    Color, CornerRadius, GradientColorSpace, GradientInterpolation, HueInterpolation,\n};\nuse smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform};\nuse smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};\nuse smithay::gpu_span_location;\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};\n\nuse super::renderer::NiriRenderer;\nuse super::shader_element::ShaderRenderElement;\nuse super::shaders::{mat3_uniform, ProgramType, Shaders};\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\nuse crate::render_helpers::renderer::AsGlesFrame as _;\n\n/// Renders a wide variety of borders and border parts.\n///\n/// This includes:\n/// * sub- or super-rect of an angled linear gradient like CSS linear-gradient(angle, a, b).\n/// * corner rounding.\n/// * as a background rectangle and as parts of a border line.\n#[derive(Debug, Clone)]\npub struct BorderRenderElement {\n    inner: ShaderRenderElement,\n    params: Parameters,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\nstruct Parameters {\n    size: Size<f64, Logical>,\n    gradient_area: Rectangle<f64, Logical>,\n    gradient_format: GradientInterpolation,\n    color_from: Color,\n    color_to: Color,\n    angle: f32,\n    geometry: Rectangle<f64, Logical>,\n    border_width: f32,\n    corner_radius: CornerRadius,\n    // Should only be used for visual improvements, i.e. corner radius anti-aliasing.\n    scale: f32,\n    alpha: f32,\n}\n\nimpl BorderRenderElement {\n    #[allow(clippy::too_many_arguments)]\n    pub fn new(\n        size: Size<f64, Logical>,\n        gradient_area: Rectangle<f64, Logical>,\n        gradient_format: GradientInterpolation,\n        color_from: Color,\n        color_to: Color,\n        angle: f32,\n        geometry: Rectangle<f64, Logical>,\n        border_width: f32,\n        corner_radius: CornerRadius,\n        scale: f32,\n        alpha: f32,\n    ) -> Self {\n        let inner = ShaderRenderElement::empty(ProgramType::Border, Kind::Unspecified);\n        let mut rv = Self {\n            inner,\n            params: Parameters {\n                size,\n                gradient_area,\n                gradient_format,\n                color_from,\n                color_to,\n                angle,\n                geometry,\n                border_width,\n                corner_radius,\n                scale,\n                alpha,\n            },\n        };\n        rv.update_inner();\n        rv\n    }\n\n    pub fn empty() -> Self {\n        let inner = ShaderRenderElement::empty(ProgramType::Border, Kind::Unspecified);\n        Self {\n            inner,\n            params: Parameters {\n                size: Default::default(),\n                gradient_area: Default::default(),\n                gradient_format: GradientInterpolation::default(),\n                color_from: Default::default(),\n                color_to: Default::default(),\n                angle: 0.,\n                geometry: Default::default(),\n                border_width: 0.,\n                corner_radius: Default::default(),\n                scale: 1.,\n                alpha: 1.,\n            },\n        }\n    }\n\n    pub fn damage_all(&mut self) {\n        self.inner.damage_all();\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn update(\n        &mut self,\n        size: Size<f64, Logical>,\n        gradient_area: Rectangle<f64, Logical>,\n        gradient_format: GradientInterpolation,\n        color_from: Color,\n        color_to: Color,\n        angle: f32,\n        geometry: Rectangle<f64, Logical>,\n        border_width: f32,\n        corner_radius: CornerRadius,\n        scale: f32,\n        alpha: f32,\n    ) {\n        let params = Parameters {\n            size,\n            gradient_area,\n            gradient_format,\n            color_from,\n            color_to,\n            angle,\n            geometry,\n            border_width,\n            corner_radius,\n            scale,\n            alpha,\n        };\n        if self.params == params {\n            return;\n        }\n\n        self.params = params;\n        self.update_inner();\n    }\n\n    fn update_inner(&mut self) {\n        let Parameters {\n            size,\n            gradient_area,\n            gradient_format,\n            color_from,\n            color_to,\n            angle,\n            geometry,\n            border_width,\n            corner_radius,\n            scale,\n            alpha,\n        } = self.params;\n\n        let grad_offset = geometry.loc - gradient_area.loc;\n        let grad_offset = Vec2::new(grad_offset.x as f32, grad_offset.y as f32);\n\n        let grad_dir = Vec2::from_angle(angle);\n\n        let (w, h) = (gradient_area.size.w as f32, gradient_area.size.h as f32);\n\n        let mut grad_area_diag = Vec2::new(w, h);\n        if (grad_dir.x < 0. && 0. <= grad_dir.y) || (0. <= grad_dir.x && grad_dir.y < 0.) {\n            grad_area_diag.x = -w;\n        }\n\n        let mut grad_vec = grad_area_diag.project_onto(grad_dir);\n        if grad_dir.y < 0. {\n            grad_vec = -grad_vec;\n        }\n\n        let area_size = Vec2::new(size.w as f32, size.h as f32);\n\n        let geo_loc = Vec2::new(geometry.loc.x as f32, geometry.loc.y as f32);\n        let geo_size = Vec2::new(geometry.size.w as f32, geometry.size.h as f32);\n\n        let input_to_geo =\n            Mat3::from_scale(area_size) * Mat3::from_translation(-geo_loc / area_size);\n\n        let colorspace = match gradient_format.color_space {\n            GradientColorSpace::Srgb => 0.,\n            GradientColorSpace::SrgbLinear => 1.,\n            GradientColorSpace::Oklab => 2.,\n            GradientColorSpace::Oklch => 3.,\n        };\n\n        let hue_interpolation = match gradient_format.hue_interpolation {\n            HueInterpolation::Shorter => 0.,\n            HueInterpolation::Longer => 1.,\n            HueInterpolation::Increasing => 2.,\n            HueInterpolation::Decreasing => 3.,\n        };\n\n        self.inner.update(\n            size,\n            None,\n            scale,\n            alpha,\n            Rc::new([\n                Uniform::new(\"colorspace\", colorspace),\n                Uniform::new(\"hue_interpolation\", hue_interpolation),\n                Uniform::new(\"color_from\", color_from.to_array_unpremul()),\n                Uniform::new(\"color_to\", color_to.to_array_unpremul()),\n                Uniform::new(\"grad_offset\", grad_offset.to_array()),\n                Uniform::new(\"grad_width\", w),\n                Uniform::new(\"grad_vec\", grad_vec.to_array()),\n                mat3_uniform(\"input_to_geo\", input_to_geo),\n                Uniform::new(\"geo_size\", geo_size.to_array()),\n                Uniform::new(\"outer_radius\", <[f32; 4]>::from(corner_radius)),\n                Uniform::new(\"border_width\", border_width),\n            ]),\n            HashMap::new(),\n        );\n    }\n\n    pub fn with_location(mut self, location: Point<f64, Logical>) -> Self {\n        self.inner = self.inner.with_location(location);\n        self\n    }\n\n    pub fn has_shader(renderer: &mut impl NiriRenderer) -> bool {\n        Shaders::get(renderer)\n            .program(ProgramType::Border)\n            .is_some()\n    }\n}\n\nimpl Default for BorderRenderElement {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\nimpl Element for BorderRenderElement {\n    fn id(&self) -> &Id {\n        self.inner.id()\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.inner.current_commit()\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.inner.geometry(scale)\n    }\n\n    fn transform(&self) -> Transform {\n        self.inner.transform()\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        self.inner.src()\n    }\n\n    fn damage_since(\n        &self,\n        scale: Scale<f64>,\n        commit: Option<CommitCounter>,\n    ) -> DamageSet<i32, Physical> {\n        self.inner.damage_since(scale, commit)\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        self.inner.opaque_regions(scale)\n    }\n\n    fn alpha(&self) -> f32 {\n        self.inner.alpha()\n    }\n\n    fn kind(&self) -> Kind {\n        self.inner.kind()\n    }\n}\n\nimpl RenderElement<GlesRenderer> for BorderRenderElement {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        let _span = tracy_client::span!(\"BorderRenderElement::draw\");\n        frame.with_gpu_span(gpu_span_location!(\"BorderRenderElement::draw\"), |frame| {\n            RenderElement::<GlesRenderer>::draw(\n                &self.inner,\n                frame,\n                src,\n                dst,\n                damage,\n                opaque_regions,\n            )\n        })\n    }\n\n    fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        self.inner.underlying_storage(renderer)\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>> for BorderRenderElement {\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'_, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        let frame = frame.as_gles_frame();\n        RenderElement::<GlesRenderer>::draw(self, frame, src, dst, damage, opaque_regions)?;\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        self.inner.underlying_storage(renderer)\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/clipped_surface.rs",
    "content": "use glam::{Mat3, Vec2};\nuse niri_config::CornerRadius;\nuse smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;\nuse smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::{\n    GlesError, GlesFrame, GlesRenderer, GlesTexProgram, Uniform,\n};\nuse smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};\n\nuse super::damage::ExtraDamage;\nuse super::renderer::{AsGlesFrame as _, NiriRenderer};\nuse super::shaders::{mat3_uniform, Shaders};\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\n\n#[derive(Debug)]\npub struct ClippedSurfaceRenderElement<R: NiriRenderer> {\n    inner: WaylandSurfaceRenderElement<R>,\n    program: GlesTexProgram,\n    corner_radius: CornerRadius,\n    geometry: Rectangle<f64, Logical>,\n    scale: f32,\n}\n\n#[derive(Debug, Default, Clone)]\npub struct RoundedCornerDamage {\n    damage: ExtraDamage,\n    corner_radius: CornerRadius,\n}\n\nimpl<R: NiriRenderer> ClippedSurfaceRenderElement<R> {\n    pub fn new(\n        elem: WaylandSurfaceRenderElement<R>,\n        scale: Scale<f64>,\n        geometry: Rectangle<f64, Logical>,\n        program: GlesTexProgram,\n        corner_radius: CornerRadius,\n    ) -> Self {\n        Self {\n            inner: elem,\n            program,\n            corner_radius,\n            geometry,\n            scale: scale.x as f32,\n        }\n    }\n\n    fn compute_uniforms(&self) -> Vec<Uniform<'static>> {\n        let scale = Scale::from(f64::from(self.scale));\n        let elem_geo = self.inner.geometry(scale);\n\n        let elem_geo_loc = Vec2::new(elem_geo.loc.x as f32, elem_geo.loc.y as f32);\n        let elem_geo_size = Vec2::new(elem_geo.size.w as f32, elem_geo.size.h as f32);\n\n        let geo = self.geometry.to_physical_precise_round(scale);\n        let geo_loc = Vec2::new(geo.loc.x, geo.loc.y);\n        let geo_size = Vec2::new(geo.size.w, geo.size.h);\n\n        let buf_size = self.inner.buffer_size();\n        let buf_size = Vec2::new(buf_size.w as f32, buf_size.h as f32);\n\n        let view = self.inner.view();\n        let src_loc = Vec2::new(view.src.loc.x as f32, view.src.loc.y as f32);\n        let src_size = Vec2::new(view.src.size.w as f32, view.src.size.h as f32);\n\n        let transform = self.inner.transform();\n        // HACK: ??? for some reason flipped ones are fine.\n        let transform = match transform {\n            Transform::_90 => Transform::_270,\n            Transform::_270 => Transform::_90,\n            x => x,\n        };\n        let transform_matrix = Mat3::from_translation(Vec2::new(0.5, 0.5))\n            * Mat3::from_cols_array(transform.matrix().as_ref())\n            * Mat3::from_translation(-Vec2::new(0.5, 0.5));\n\n        // FIXME: y_inverted\n        let input_to_geo = transform_matrix * Mat3::from_scale(elem_geo_size / geo_size)\n            * Mat3::from_translation((elem_geo_loc - geo_loc) / elem_geo_size)\n            // Apply viewporter src.\n            * Mat3::from_scale(buf_size / src_size)\n            * Mat3::from_translation(-src_loc / buf_size);\n\n        let geo_size = (self.geometry.size.w as f32, self.geometry.size.h as f32);\n\n        vec![\n            Uniform::new(\"niri_scale\", self.scale),\n            Uniform::new(\"geo_size\", geo_size),\n            Uniform::new(\"corner_radius\", <[f32; 4]>::from(self.corner_radius)),\n            mat3_uniform(\"input_to_geo\", input_to_geo),\n        ]\n    }\n\n    pub fn shader(renderer: &mut R) -> Option<&GlesTexProgram> {\n        Shaders::get(renderer).clipped_surface.as_ref()\n    }\n\n    pub fn will_clip(\n        elem: &WaylandSurfaceRenderElement<R>,\n        scale: Scale<f64>,\n        geometry: Rectangle<f64, Logical>,\n        corner_radius: CornerRadius,\n    ) -> bool {\n        let elem_geo = elem.geometry(scale);\n        let geo = geometry.to_physical_precise_round(scale);\n\n        if corner_radius == CornerRadius::default() {\n            !geo.contains_rect(elem_geo)\n        } else {\n            let corners = Self::rounded_corners(geometry, corner_radius);\n            let corners = corners\n                .into_iter()\n                .map(|rect| rect.to_physical_precise_up(scale));\n            let geo = Rectangle::subtract_rects_many([geo], corners);\n            !Rectangle::subtract_rects_many([elem_geo], geo).is_empty()\n        }\n    }\n\n    fn rounded_corners(\n        geo: Rectangle<f64, Logical>,\n        corner_radius: CornerRadius,\n    ) -> [Rectangle<f64, Logical>; 4] {\n        let top_left = corner_radius.top_left as f64;\n        let top_right = corner_radius.top_right as f64;\n        let bottom_right = corner_radius.bottom_right as f64;\n        let bottom_left = corner_radius.bottom_left as f64;\n\n        [\n            Rectangle::new(geo.loc, Size::from((top_left, top_left))),\n            Rectangle::new(\n                Point::from((geo.loc.x + geo.size.w - top_right, geo.loc.y)),\n                Size::from((top_right, top_right)),\n            ),\n            Rectangle::new(\n                Point::from((\n                    geo.loc.x + geo.size.w - bottom_right,\n                    geo.loc.y + geo.size.h - bottom_right,\n                )),\n                Size::from((bottom_right, bottom_right)),\n            ),\n            Rectangle::new(\n                Point::from((geo.loc.x, geo.loc.y + geo.size.h - bottom_left)),\n                Size::from((bottom_left, bottom_left)),\n            ),\n        ]\n    }\n}\n\nimpl<R: NiriRenderer> Element for ClippedSurfaceRenderElement<R> {\n    fn id(&self) -> &Id {\n        self.inner.id()\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.inner.current_commit()\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.inner.geometry(scale)\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        self.inner.src()\n    }\n\n    fn transform(&self) -> Transform {\n        self.inner.transform()\n    }\n\n    fn damage_since(\n        &self,\n        scale: Scale<f64>,\n        commit: Option<CommitCounter>,\n    ) -> DamageSet<i32, Physical> {\n        // FIXME: radius changes need to cause damage.\n        let damage = self.inner.damage_since(scale, commit);\n\n        // Intersect with geometry, since we're clipping by it.\n        let mut geo = self.geometry.to_physical_precise_round(scale);\n        geo.loc -= self.geometry(scale).loc;\n        damage\n            .into_iter()\n            .filter_map(|rect| rect.intersection(geo))\n            .collect()\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        let regions = self.inner.opaque_regions(scale);\n\n        // Intersect with geometry, since we're clipping by it.\n        let mut geo = self.geometry.to_physical_precise_round(scale);\n        geo.loc -= self.geometry(scale).loc;\n        let regions = regions\n            .into_iter()\n            .filter_map(|rect| rect.intersection(geo));\n\n        // Subtract the rounded corners.\n        if self.corner_radius == CornerRadius::default() {\n            regions.collect()\n        } else {\n            let corners = Self::rounded_corners(self.geometry, self.corner_radius);\n\n            let elem_loc = self.geometry(scale).loc;\n            let corners = corners.into_iter().map(|rect| {\n                let mut rect = rect.to_physical_precise_up(scale);\n                rect.loc -= elem_loc;\n                rect\n            });\n\n            OpaqueRegions::from_slice(&Rectangle::subtract_rects_many(regions, corners))\n        }\n    }\n\n    fn alpha(&self) -> f32 {\n        self.inner.alpha()\n    }\n\n    fn kind(&self) -> Kind {\n        self.inner.kind()\n    }\n}\n\nimpl RenderElement<GlesRenderer> for ClippedSurfaceRenderElement<GlesRenderer> {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        frame.override_default_tex_program(self.program.clone(), self.compute_uniforms());\n        RenderElement::<GlesRenderer>::draw(&self.inner, frame, src, dst, damage, opaque_regions)?;\n        frame.clear_tex_program_override();\n        Ok(())\n    }\n\n    fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>>\n    for ClippedSurfaceRenderElement<TtyRenderer<'render>>\n{\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'render, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        frame\n            .as_gles_frame()\n            .override_default_tex_program(self.program.clone(), self.compute_uniforms());\n        RenderElement::draw(&self.inner, frame, src, dst, damage, opaque_regions)?;\n        frame.as_gles_frame().clear_tex_program_override();\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        _renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n\nimpl RoundedCornerDamage {\n    pub fn set_size(&mut self, size: Size<f64, Logical>) {\n        self.damage.set_size(size);\n    }\n\n    pub fn set_corner_radius(&mut self, corner_radius: CornerRadius) {\n        if self.corner_radius == corner_radius {\n            return;\n        }\n\n        // FIXME: make the damage granular.\n        self.corner_radius = corner_radius;\n        self.damage.damage_all();\n    }\n\n    pub fn element(&self) -> ExtraDamage {\n        self.damage.clone()\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/damage.rs",
    "content": "use smithay::backend::renderer::element::{Element, Id, RenderElement};\nuse smithay::backend::renderer::utils::CommitCounter;\nuse smithay::backend::renderer::Renderer;\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size};\n\n#[derive(Debug, Clone)]\npub struct ExtraDamage {\n    id: Id,\n    commit: CommitCounter,\n    geometry: Rectangle<f64, Logical>,\n}\n\nimpl ExtraDamage {\n    pub fn new() -> Self {\n        Self {\n            id: Id::new(),\n            commit: Default::default(),\n            geometry: Default::default(),\n        }\n    }\n\n    pub fn set_size(&mut self, size: Size<f64, Logical>) {\n        if self.geometry.size == size {\n            return;\n        }\n\n        self.geometry.size = size;\n        self.commit.increment();\n    }\n\n    pub fn damage_all(&mut self) {\n        self.commit.increment();\n    }\n\n    pub fn with_location(mut self, location: Point<f64, Logical>) -> Self {\n        self.geometry.loc = location;\n        self\n    }\n}\n\nimpl Default for ExtraDamage {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl Element for ExtraDamage {\n    fn id(&self) -> &Id {\n        &self.id\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.commit\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        Rectangle::from_size(Size::from((1., 1.)))\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.geometry.to_physical_precise_up(scale)\n    }\n}\n\nimpl<R: Renderer> RenderElement<R> for ExtraDamage {\n    fn draw(\n        &self,\n        _frame: &mut R::Frame<'_, '_>,\n        _src: Rectangle<f64, Buffer>,\n        _dst: Rectangle<i32, Physical>,\n        _damage: &[Rectangle<i32, Physical>],\n        _opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), R::Error> {\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/debug.rs",
    "content": "use smithay::backend::renderer::damage::OutputDamageTracker;\nuse smithay::backend::renderer::element::{Element, Id, Kind};\nuse smithay::backend::renderer::utils::CommitCounter;\nuse smithay::backend::renderer::Color32F;\nuse smithay::utils::Scale;\n\nuse super::renderer::NiriRenderer;\nuse super::solid_color::SolidColorRenderElement;\nuse crate::niri::OutputRenderElements;\n\npub fn push_opaque_regions<R: NiriRenderer>(\n    elem: &OutputRenderElements<R>,\n    scale: Scale<f64>,\n    push: &mut dyn FnMut(OutputRenderElements<R>),\n) {\n    // HACK\n    if format!(\"{elem:?}\").contains(\"ExtraDamage\") {\n        return;\n    }\n\n    let geo = elem.geometry(scale);\n    let mut opaque = elem.opaque_regions(scale).to_vec();\n\n    for rect in &mut opaque {\n        rect.loc += geo.loc;\n    }\n\n    let semitransparent = geo.subtract_rects(opaque.iter().copied());\n\n    for rect in opaque {\n        let color = SolidColorRenderElement::new(\n            Id::new(),\n            rect.to_f64().to_logical(scale),\n            CommitCounter::default(),\n            Color32F::from([0., 0., 0.2, 0.2]),\n            Kind::Unspecified,\n        );\n        push(color.into());\n    }\n\n    for rect in semitransparent {\n        let color = SolidColorRenderElement::new(\n            Id::new(),\n            rect.to_f64().to_logical(scale),\n            CommitCounter::default(),\n            Color32F::from([0.3, 0., 0., 0.3]),\n            Kind::Unspecified,\n        );\n        push(color.into());\n    }\n}\n\npub fn draw_damage<R: NiriRenderer>(\n    damage_tracker: &mut OutputDamageTracker,\n    elements: &mut Vec<OutputRenderElements<R>>,\n) {\n    let _span = tracy_client::span!(\"draw_damage\");\n\n    let Ok((_, scale, _)) = damage_tracker.mode().try_into() else {\n        return;\n    };\n\n    let Ok((Some(damage), _)) = damage_tracker.damage_output(1, elements) else {\n        return;\n    };\n\n    for rect in damage {\n        let color = SolidColorRenderElement::new(\n            Id::new(),\n            rect.to_f64().to_logical(scale),\n            CommitCounter::default(),\n            Color32F::from([0.3, 0., 0., 0.3]),\n            Kind::Unspecified,\n        );\n        elements.insert(0, OutputRenderElements::SolidColor(color));\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/gradient_fade_texture.rs",
    "content": "use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::{\n    GlesError, GlesFrame, GlesRenderer, GlesTexProgram, GlesTexture, Uniform,\n};\nuse smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};\nuse smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};\n\nuse super::texture::TextureRenderElement;\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\nuse crate::render_helpers::renderer::AsGlesFrame as _;\nuse crate::render_helpers::shaders::Shaders;\n\n#[derive(Debug, Clone)]\npub struct GradientFadeTextureRenderElement {\n    inner: TextureRenderElement<GlesTexture>,\n    program: GradientFadeShader,\n    cutoff: (f32, f32),\n}\n\n#[derive(Debug, Clone)]\npub struct GradientFadeShader(GlesTexProgram);\n\nimpl GradientFadeTextureRenderElement {\n    pub fn new(texture: TextureRenderElement<GlesTexture>, program: GradientFadeShader) -> Self {\n        let logical_w = texture.buffer().logical_size().w;\n        let logical_src_w = texture.logical_src().size.w;\n        let cutoff = if logical_src_w < logical_w {\n            // Texture is clipped, add a fade.\n            let cutoff = 1. - f64::min(18. / logical_src_w, 1.);\n            let full = logical_src_w / logical_w;\n            ((cutoff * full) as f32, full as f32)\n        } else {\n            // Texture is displayed full-size, no cutoff necessary.\n            (1., 1.)\n        };\n        Self {\n            inner: texture,\n            program,\n            cutoff,\n        }\n    }\n\n    pub fn shader(renderer: &mut GlesRenderer) -> Option<GradientFadeShader> {\n        let program = Shaders::get(renderer).gradient_fade.clone();\n        program.map(GradientFadeShader)\n    }\n}\n\nimpl Element for GradientFadeTextureRenderElement {\n    fn id(&self) -> &Id {\n        self.inner.id()\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.inner.current_commit()\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.inner.geometry(scale)\n    }\n\n    fn transform(&self) -> Transform {\n        self.inner.transform()\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        self.inner.src()\n    }\n\n    fn damage_since(\n        &self,\n        scale: Scale<f64>,\n        commit: Option<CommitCounter>,\n    ) -> DamageSet<i32, Physical> {\n        self.inner.damage_since(scale, commit)\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        self.inner.opaque_regions(scale)\n    }\n\n    fn alpha(&self) -> f32 {\n        self.inner.alpha()\n    }\n\n    fn kind(&self) -> Kind {\n        self.inner.kind()\n    }\n}\n\nimpl RenderElement<GlesRenderer> for GradientFadeTextureRenderElement {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        let uniforms = vec![Uniform::new(\"cutoff\", self.cutoff)];\n        frame.override_default_tex_program(self.program.0.clone(), uniforms);\n        RenderElement::<GlesRenderer>::draw(&self.inner, frame, src, dst, damage, opaque_regions)?;\n        frame.clear_tex_program_override();\n        Ok(())\n    }\n\n    fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>> for GradientFadeTextureRenderElement {\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'render, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        let gles_frame = frame.as_gles_frame();\n        RenderElement::<GlesRenderer>::draw(&self, gles_frame, src, dst, damage, opaque_regions)?;\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        _renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/memory.rs",
    "content": "use std::sync::Arc;\n\nuse smithay::backend::allocator::format::get_bpp;\nuse smithay::backend::allocator::Fourcc;\nuse smithay::utils::{Buffer, Logical, Scale, Size, Transform};\n\n#[derive(Clone)]\npub struct MemoryBuffer {\n    data: Arc<[u8]>,\n    format: Fourcc,\n    size: Size<i32, Buffer>,\n    scale: Scale<f64>,\n    transform: Transform,\n}\n\nimpl MemoryBuffer {\n    pub fn new(\n        data: impl Into<Arc<[u8]>>,\n        format: Fourcc,\n        size: impl Into<Size<i32, Buffer>>,\n        scale: impl Into<Scale<f64>>,\n        transform: Transform,\n    ) -> Self {\n        let data = data.into();\n\n        let size = size.into();\n        let stride =\n            size.w * (get_bpp(format).expect(\"Format with unknown bits per pixel\") / 8) as i32;\n        assert!(data.len() >= (stride * size.h) as usize);\n\n        Self {\n            data,\n            format,\n            size,\n            scale: scale.into(),\n            transform,\n        }\n    }\n\n    pub fn data(&self) -> &[u8] {\n        &self.data\n    }\n\n    pub fn format(&self) -> Fourcc {\n        self.format\n    }\n\n    pub fn size(&self) -> Size<i32, Buffer> {\n        self.size\n    }\n\n    pub fn scale(&self) -> Scale<f64> {\n        self.scale\n    }\n\n    pub fn transform(&self) -> Transform {\n        self.transform\n    }\n\n    pub fn logical_size(&self) -> Size<f64, Logical> {\n        self.size.to_f64().to_logical(self.scale, self.transform)\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/mod.rs",
    "content": "use std::ptr;\n\nuse anyhow::{ensure, Context};\nuse niri_config::BlockOutFrom;\nuse smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::allocator::{Buffer, Fourcc};\nuse smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};\nuse smithay::backend::renderer::element::{Element, Kind, RenderElement};\nuse smithay::backend::renderer::gles::{GlesMapping, GlesRenderer, GlesTarget, GlesTexture};\nuse smithay::backend::renderer::sync::SyncPoint;\nuse smithay::backend::renderer::{Bind, Color32F, ExportMem, Frame, Offscreen, Renderer};\nuse smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;\nuse smithay::reexports::wayland_server::protocol::wl_shm;\nuse smithay::utils::{Logical, Physical, Point, Rectangle, Scale, Size, Transform};\nuse smithay::wayland::shm;\nuse solid_color::{SolidColorBuffer, SolidColorRenderElement};\n\nuse self::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse self::texture::{TextureBuffer, TextureRenderElement};\n\npub mod border;\npub mod clipped_surface;\npub mod damage;\npub mod debug;\npub mod gradient_fade_texture;\npub mod memory;\npub mod offscreen;\npub mod primary_gpu_texture;\npub mod render_elements;\npub mod renderer;\npub mod resize;\npub mod resources;\npub mod shader_element;\npub mod shaders;\npub mod shadow;\npub mod snapshot;\npub mod solid_color;\npub mod surface;\npub mod texture;\n\n/// What we're rendering for.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum RenderTarget {\n    /// Rendering to display on screen.\n    Output,\n    /// Rendering for a screencast.\n    Screencast,\n    /// Rendering for any other screen capture.\n    ScreenCapture,\n}\n\n/// Buffer with location, src and dst.\n#[derive(Debug)]\npub struct BakedBuffer<B> {\n    pub buffer: B,\n    pub location: Point<f64, Logical>,\n    pub src: Option<Rectangle<f64, Logical>>,\n    pub dst: Option<Size<i32, Logical>>,\n}\n\npub trait ToRenderElement {\n    type RenderElement;\n\n    fn to_render_element(\n        &self,\n        location: Point<f64, Logical>,\n        scale: Scale<f64>,\n        alpha: f32,\n        kind: Kind,\n    ) -> Self::RenderElement;\n}\n\nimpl RenderTarget {\n    pub fn should_block_out(self, block_out_from: Option<BlockOutFrom>) -> bool {\n        match block_out_from {\n            None => false,\n            Some(BlockOutFrom::Screencast) => self == RenderTarget::Screencast,\n            Some(BlockOutFrom::ScreenCapture) => self != RenderTarget::Output,\n        }\n    }\n}\n\nimpl ToRenderElement for BakedBuffer<TextureBuffer<GlesTexture>> {\n    type RenderElement = PrimaryGpuTextureRenderElement;\n\n    fn to_render_element(\n        &self,\n        location: Point<f64, Logical>,\n        _scale: Scale<f64>,\n        alpha: f32,\n        kind: Kind,\n    ) -> Self::RenderElement {\n        let elem = TextureRenderElement::from_texture_buffer(\n            self.buffer.clone(),\n            location + self.location,\n            alpha,\n            self.src,\n            self.dst.map(|dst| dst.to_f64()),\n            kind,\n        );\n        PrimaryGpuTextureRenderElement(elem)\n    }\n}\n\nimpl ToRenderElement for BakedBuffer<SolidColorBuffer> {\n    type RenderElement = SolidColorRenderElement;\n\n    fn to_render_element(\n        &self,\n        location: Point<f64, Logical>,\n        _scale: Scale<f64>,\n        alpha: f32,\n        kind: Kind,\n    ) -> Self::RenderElement {\n        SolidColorRenderElement::from_buffer(&self.buffer, location + self.location, alpha, kind)\n    }\n}\n\npub fn encompassing_geo(\n    scale: Scale<f64>,\n    elements: impl Iterator<Item = impl Element>,\n) -> Rectangle<i32, Physical> {\n    elements\n        .map(|ele| ele.geometry(scale))\n        .reduce(|a, b| a.merge(b))\n        .unwrap_or_default()\n}\n\npub fn render_to_encompassing_texture(\n    renderer: &mut GlesRenderer,\n    scale: Scale<f64>,\n    transform: Transform,\n    fourcc: Fourcc,\n    elements: &[impl RenderElement<GlesRenderer>],\n) -> anyhow::Result<(GlesTexture, SyncPoint, Rectangle<i32, Physical>)> {\n    let geo = encompassing_geo(scale, elements.iter());\n    let elements = elements.iter().rev().map(|ele| {\n        RelocateRenderElement::from_element(ele, geo.loc.upscale(-1), Relocate::Relative)\n    });\n\n    let (texture, sync_point) =\n        render_to_texture(renderer, geo.size, scale, transform, fourcc, elements)?;\n\n    Ok((texture, sync_point, geo))\n}\n\npub fn render_to_texture(\n    renderer: &mut GlesRenderer,\n    size: Size<i32, Physical>,\n    scale: Scale<f64>,\n    transform: Transform,\n    fourcc: Fourcc,\n    elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,\n) -> anyhow::Result<(GlesTexture, SyncPoint)> {\n    let _span = tracy_client::span!();\n\n    let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal);\n\n    let mut texture: GlesTexture = renderer\n        .create_buffer(fourcc, buffer_size)\n        .context(\"error creating texture\")?;\n\n    let sync_point = {\n        let mut target = renderer\n            .bind(&mut texture)\n            .context(\"error binding texture\")?;\n\n        render_elements(renderer, &mut target, size, scale, transform, elements)?\n    };\n\n    Ok((texture, sync_point))\n}\n\npub fn render_and_download(\n    renderer: &mut GlesRenderer,\n    size: Size<i32, Physical>,\n    scale: Scale<f64>,\n    transform: Transform,\n    fourcc: Fourcc,\n    elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,\n) -> anyhow::Result<GlesMapping> {\n    let _span = tracy_client::span!();\n\n    let (mut texture, _) = render_to_texture(renderer, size, scale, transform, fourcc, elements)?;\n\n    let buffer_size = size.to_logical(1).to_buffer(1, Transform::Normal);\n    // FIXME: would be nice to avoid binding the second time here (after render_to_texture()), but\n    // borrowing makes this inconvenient.\n    let target = renderer\n        .bind(&mut texture)\n        .context(\"error binding texture\")?;\n    let mapping = renderer\n        .copy_framebuffer(&target, Rectangle::from_size(buffer_size), fourcc)\n        .context(\"error copying framebuffer\")?;\n    Ok(mapping)\n}\n\npub fn render_to_vec(\n    renderer: &mut GlesRenderer,\n    size: Size<i32, Physical>,\n    scale: Scale<f64>,\n    transform: Transform,\n    fourcc: Fourcc,\n    elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,\n) -> anyhow::Result<Vec<u8>> {\n    let _span = tracy_client::span!();\n\n    let mapping = render_and_download(renderer, size, scale, transform, fourcc, elements)\n        .context(\"error rendering\")?;\n    let copy = renderer\n        .map_texture(&mapping)\n        .context(\"error mapping texture\")?;\n    Ok(copy.to_vec())\n}\n\npub fn render_to_dmabuf(\n    renderer: &mut GlesRenderer,\n    mut dmabuf: Dmabuf,\n    size: Size<i32, Physical>,\n    scale: Scale<f64>,\n    transform: Transform,\n    elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,\n) -> anyhow::Result<SyncPoint> {\n    let _span = tracy_client::span!();\n    ensure!(\n        dmabuf.width() == size.w as u32 && dmabuf.height() == size.h as u32,\n        \"invalid buffer size\"\n    );\n    let mut target = renderer\n        .bind(&mut dmabuf)\n        .context(\"error binding texture\")?;\n    render_elements(renderer, &mut target, size, scale, transform, elements)\n}\n\npub fn render_to_shm(\n    renderer: &mut GlesRenderer,\n    buffer: &WlBuffer,\n    size: Size<i32, Physical>,\n    scale: Scale<f64>,\n    transform: Transform,\n    elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,\n) -> anyhow::Result<()> {\n    let _span = tracy_client::span!();\n    shm::with_buffer_contents_mut(buffer, |shm_buffer, shm_len, buffer_data| {\n        ensure!(\n            // The buffer prefers pixels in little endian ...\n            buffer_data.format == wl_shm::Format::Xrgb8888\n                && buffer_data.width == size.w\n                && buffer_data.height == size.h\n                && buffer_data.stride == size.w * 4\n                && shm_len == buffer_data.stride as usize * buffer_data.height as usize,\n            \"invalid buffer format or size\"\n        );\n        let mapping =\n            render_and_download(renderer, size, scale, transform, Fourcc::Xrgb8888, elements)?;\n\n        let bytes = renderer\n            .map_texture(&mapping)\n            .context(\"error mapping texture\")?;\n\n        unsafe {\n            let _span = tracy_client::span!(\"copy_nonoverlapping\");\n            ptr::copy_nonoverlapping(bytes.as_ptr(), shm_buffer.cast(), shm_len);\n        }\n\n        Ok(())\n    })\n    .context(\"expected shm buffer, but didn't get one\")?\n}\n\npub fn clear_dmabuf(renderer: &mut GlesRenderer, mut dmabuf: Dmabuf) -> anyhow::Result<SyncPoint> {\n    let size = dmabuf.size();\n    let size = size.to_logical(1, Transform::Normal).to_physical(1);\n    let mut target = renderer.bind(&mut dmabuf).context(\"error binding dmabuf\")?;\n    let mut frame = renderer\n        .render(&mut target, size, Transform::Normal)\n        .context(\"error starting frame\")?;\n    frame\n        .clear(Color32F::TRANSPARENT, &[Rectangle::from_size(size)])\n        .context(\"error clearing\")?;\n    frame.finish().context(\"error finishing frame\")\n}\n\nfn render_elements(\n    renderer: &mut GlesRenderer,\n    target: &mut GlesTarget,\n    size: Size<i32, Physical>,\n    scale: Scale<f64>,\n    transform: Transform,\n    elements: impl Iterator<Item = impl RenderElement<GlesRenderer>>,\n) -> anyhow::Result<SyncPoint> {\n    let transform = transform.invert();\n    let output_rect = Rectangle::from_size(transform.transform_size(size));\n\n    let mut frame = renderer\n        .render(target, size, transform)\n        .context(\"error starting frame\")?;\n\n    frame\n        .clear(Color32F::TRANSPARENT, &[output_rect])\n        .context(\"error clearing\")?;\n\n    for element in elements {\n        let src = element.src();\n        let dst = element.geometry(scale);\n\n        if let Some(mut damage) = output_rect.intersection(dst) {\n            damage.loc -= dst.loc;\n            element\n                .draw(&mut frame, src, dst, &[damage], &[])\n                .context(\"error drawing element\")?;\n        }\n    }\n\n    frame.finish().context(\"error finishing frame\")\n}\n"
  },
  {
    "path": "src/render_helpers/offscreen.rs",
    "content": "use std::cell::RefCell;\n\nuse anyhow::Context as _;\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::renderer::damage::OutputDamageTracker;\nuse smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};\nuse smithay::backend::renderer::element::{\n    Element, Id, Kind, RenderElement, RenderElementStates, UnderlyingStorage,\n};\nuse smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture};\nuse smithay::backend::renderer::sync::SyncPoint;\nuse smithay::backend::renderer::utils::{\n    CommitCounter, DamageBag, DamageSet, DamageSnapshot, OpaqueRegions,\n};\nuse smithay::backend::renderer::{\n    Bind as _, Color32F, ContextId, Frame as _, Offscreen as _, Renderer, Texture as _,\n};\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};\n\nuse super::encompassing_geo;\nuse super::renderer::AsGlesFrame as _;\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\n\n/// Buffer for offscreen rendering.\n#[derive(Debug)]\npub struct OffscreenBuffer {\n    id: Id,\n\n    /// The cached texture buffer.\n    ///\n    /// Lazily created when `render` is called. Recreated when necessary.\n    inner: RefCell<Option<Inner>>,\n}\n\n#[derive(Debug)]\nstruct Inner {\n    /// The texture with offscreened contents.\n    texture: GlesTexture,\n    /// Id of the renderer context that the texture comes from.\n    renderer_context_id: ContextId<GlesTexture>,\n    /// Scale of the texture.\n    scale: Scale<f64>,\n    /// Damage tracker for drawing to the texture.\n    damage: OutputDamageTracker,\n    /// Damage of this offscreen element itself facing outside.\n    outer_damage: DamageBag<i32, Buffer>,\n}\n\n#[derive(Debug, Clone)]\npub struct OffscreenRenderElement {\n    id: Id,\n    texture: GlesTexture,\n    renderer_context_id: ContextId<GlesTexture>,\n    scale: Scale<f64>,\n    damage: DamageSnapshot<i32, Buffer>,\n    offset: Point<f64, Logical>,\n    src_size: Size<i32, Buffer>,\n    alpha: f32,\n    kind: Kind,\n}\n\n#[derive(Debug)]\npub struct OffscreenData {\n    /// Id of the offscreen element.\n    pub id: Id,\n    /// States for the render into the offscreen buffer.\n    pub states: RenderElementStates,\n}\n\nimpl OffscreenBuffer {\n    pub fn render(\n        &self,\n        renderer: &mut GlesRenderer,\n        scale: Scale<f64>,\n        elements: &[impl RenderElement<GlesRenderer>],\n    ) -> anyhow::Result<(OffscreenRenderElement, SyncPoint, OffscreenData)> {\n        let _span = tracy_client::span!(\"OffscreenBuffer::render\");\n\n        let geo = encompassing_geo(scale, elements.iter());\n        let elements = Vec::from_iter(elements.iter().map(|ele| {\n            RelocateRenderElement::from_element(ele, geo.loc.upscale(-1), Relocate::Relative)\n        }));\n\n        let src_size = geo.size;\n        let src_size = src_size.to_logical(1).to_buffer(1, Transform::Normal);\n        let offset = geo.loc.to_f64().to_logical(scale);\n\n        let mut inner = self.inner.borrow_mut();\n\n        // Check if we need to create or recreate the texture.\n        let size_string;\n        let mut reason = \"\";\n        if let Some(Inner {\n            texture,\n            renderer_context_id,\n            ..\n        }) = inner.as_mut()\n        {\n            let old_size = texture.size();\n            if old_size.w < src_size.w || old_size.h < src_size.h {\n                size_string = format!(\n                    \"size increased from {} × {} to {} × {}\",\n                    old_size.w, old_size.h, src_size.w, src_size.h\n                );\n                reason = &size_string;\n\n                *inner = None;\n            } else if !texture.is_unique_reference() {\n                reason = \"not unique\";\n\n                *inner = None;\n            } else if *renderer_context_id != renderer.context_id() {\n                reason = \"renderer id changed\";\n\n                *inner = None;\n            }\n        } else {\n            reason = \"first render\";\n        }\n\n        let inner = if let Some(inner) = inner.as_mut() {\n            inner\n        } else {\n            trace!(\"creating new texture: {reason}\");\n            let span = tracy_client::span!(\"creating offscreen buffer\");\n            span.emit_text(reason);\n\n            let texture: GlesTexture = renderer\n                .create_buffer(Fourcc::Abgr8888, src_size)\n                .context(\"error creating texture\")?;\n\n            let buffer_size = src_size.to_logical(1, Transform::Normal).to_physical(1);\n            let damage = OutputDamageTracker::new(buffer_size, scale, Transform::Normal);\n\n            inner.insert(Inner {\n                texture,\n                renderer_context_id: renderer.context_id(),\n                scale,\n                damage,\n                outer_damage: DamageBag::default(),\n            })\n        };\n\n        // When leaving the old texture as is, its size might be bigger than src_size.\n        let texture_size = inner.texture.size();\n        let buffer_size = texture_size.to_logical(1, Transform::Normal).to_physical(1);\n\n        // Recreate the damage tracker if the scale changes. We already recreate it for buffer size\n        // changes, and transform is always Normal.\n        if inner.scale != scale {\n            inner.scale = scale;\n\n            trace!(\"recreating damage tracker due to scale change\");\n            inner.damage = OutputDamageTracker::new(buffer_size, scale, Transform::Normal);\n            inner.outer_damage = DamageBag::default();\n        }\n\n        let res = {\n            let mut target = renderer.bind(&mut inner.texture)?;\n            inner.damage.render_output(\n                renderer,\n                &mut target,\n                1,\n                &elements,\n                Color32F::TRANSPARENT,\n            )?\n        };\n\n        // Add the resulting damage to the outer tracker.\n        if let Some(damage) = res.damage {\n            // OutputDamageTracker gives us Physical coordinate space, but it's actually the Buffer\n            // space because we were rendering to a texture.\n            let size = buffer_size.to_logical(1);\n            let damage = damage\n                .iter()\n                .map(|rect| rect.to_logical(1).to_buffer(1, Transform::Normal, &size));\n            inner.outer_damage.add(damage);\n        }\n\n        let elem = OffscreenRenderElement {\n            id: self.id.clone(),\n            texture: inner.texture.clone(),\n            renderer_context_id: inner.renderer_context_id.clone(),\n            scale,\n            damage: inner.outer_damage.snapshot(),\n            offset,\n            src_size,\n            alpha: 1.,\n            kind: Kind::Unspecified,\n        };\n\n        let data = OffscreenData {\n            id: self.id.clone(),\n            states: res.states,\n        };\n\n        Ok((elem, res.sync, data))\n    }\n}\n\nimpl Default for OffscreenBuffer {\n    fn default() -> Self {\n        OffscreenBuffer {\n            inner: RefCell::new(None),\n            id: Id::new(),\n        }\n    }\n}\n\nimpl OffscreenRenderElement {\n    pub fn texture(&self) -> &GlesTexture {\n        &self.texture\n    }\n\n    pub fn offset(&self) -> Point<f64, Logical> {\n        self.offset\n    }\n\n    pub fn with_alpha(mut self, alpha: f32) -> Self {\n        self.alpha = alpha;\n        self\n    }\n\n    pub fn with_offset(mut self, offset: Point<f64, Logical>) -> Self {\n        self.offset = offset;\n        self\n    }\n\n    pub fn logical_size(&self) -> Size<f64, Logical> {\n        self.src_size\n            .to_f64()\n            .to_logical(self.scale, Transform::Normal)\n    }\n\n    fn damage_since(&self, commit: Option<CommitCounter>) -> DamageSet<i32, Buffer> {\n        self.damage\n            .damage_since(commit)\n            .unwrap_or_else(|| DamageSet::from_slice(&[Rectangle::from_size(self.texture.size())]))\n    }\n}\n\nimpl Element for OffscreenRenderElement {\n    fn id(&self) -> &Id {\n        &self.id\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.damage.current_commit()\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        let logical_geo = Rectangle::new(self.offset, self.logical_size());\n        logical_geo.to_physical_precise_round(scale)\n    }\n\n    fn transform(&self) -> Transform {\n        Transform::Normal\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        Rectangle::from_size(self.src_size).to_f64()\n    }\n\n    fn damage_since(\n        &self,\n        scale: Scale<f64>,\n        commit: Option<CommitCounter>,\n    ) -> DamageSet<i32, Physical> {\n        let texture_size = self.texture.size().to_f64();\n        let src = self.src();\n\n        self.damage_since(commit)\n            .into_iter()\n            .filter_map(|region| {\n                let mut region = region.to_f64().intersection(src)?;\n\n                region.loc -= src.loc;\n                region = region.upscale(texture_size / src.size);\n\n                let logical = region.to_logical(self.scale, Transform::Normal, &src.size);\n                Some(logical.to_physical_precise_up(scale))\n            })\n            .collect()\n    }\n\n    fn opaque_regions(&self, _scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        OpaqueRegions::default()\n    }\n\n    fn alpha(&self) -> f32 {\n        self.alpha\n    }\n\n    fn kind(&self) -> Kind {\n        self.kind\n    }\n}\n\nimpl RenderElement<GlesRenderer> for OffscreenRenderElement {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dest: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        if frame.context_id() != self.renderer_context_id {\n            warn!(\"trying to render texture from different renderer\");\n            return Ok(());\n        }\n\n        frame.render_texture_from_to(\n            &self.texture,\n            src,\n            dest,\n            damage,\n            opaque_regions,\n            Transform::Normal,\n            self.alpha,\n            None,\n            &[],\n        )\n    }\n\n    fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>> for OffscreenRenderElement {\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'_, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        let gles_frame = frame.as_gles_frame();\n        RenderElement::<GlesRenderer>::draw(&self, gles_frame, src, dst, damage, opaque_regions)?;\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        _renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/primary_gpu_texture.rs",
    "content": "use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture};\nuse smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};\nuse smithay::utils::{Buffer, Physical, Rectangle, Scale, Transform};\n\nuse super::renderer::AsGlesFrame;\nuse super::texture::TextureRenderElement;\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\n\n/// Wrapper for a texture from the primary GPU for rendering with the primary GPU.\n#[derive(Debug, Clone)]\npub struct PrimaryGpuTextureRenderElement(pub TextureRenderElement<GlesTexture>);\n\nimpl Element for PrimaryGpuTextureRenderElement {\n    fn id(&self) -> &Id {\n        self.0.id()\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.0.current_commit()\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.0.geometry(scale)\n    }\n\n    fn transform(&self) -> Transform {\n        self.0.transform()\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        self.0.src()\n    }\n\n    fn damage_since(\n        &self,\n        scale: Scale<f64>,\n        commit: Option<CommitCounter>,\n    ) -> DamageSet<i32, Physical> {\n        self.0.damage_since(scale, commit)\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        self.0.opaque_regions(scale)\n    }\n\n    fn alpha(&self) -> f32 {\n        self.0.alpha()\n    }\n\n    fn kind(&self) -> Kind {\n        self.0.kind()\n    }\n}\n\nimpl RenderElement<GlesRenderer> for PrimaryGpuTextureRenderElement {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        let gles_frame = frame.as_gles_frame();\n        RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage, opaque_regions)?;\n        Ok(())\n    }\n\n    fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>> for PrimaryGpuTextureRenderElement {\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'_, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        let gles_frame = frame.as_gles_frame();\n        RenderElement::<GlesRenderer>::draw(&self.0, gles_frame, src, dst, damage, opaque_regions)?;\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        _renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/render_elements.rs",
    "content": "// We need to implement RenderElement manually due to AsGlesFrame requirement.\n// This macro does it for us.\n#[macro_export]\nmacro_rules! niri_render_elements {\n    // The two callable variants: with <R> and without <R>. They include From impls because nested\n    // repetitions ($type and $variant with + and $R with ?) don't work properly.\n    ($name:ident<R> => { $($variant:ident = $type:ty),+ $(,)? }) => {\n        $crate::niri_render_elements!(@impl $name () ($name<R>) => { $($variant = $type),+ });\n\n        $(impl<R: $crate::render_helpers::renderer::NiriRenderer> From<$type> for $name<R> {\n            fn from(x: $type) -> Self {\n                Self::$variant(x)\n            }\n        })+\n    };\n\n    ($name:ident => { $($variant:ident = $type:ty),+ $(,)? }) => {\n        $crate::niri_render_elements!(@impl $name ($name) () => { $($variant = $type),+ });\n\n        $(impl From<$type> for $name {\n            fn from(x: $type) -> Self {\n                Self::$variant(x)\n            }\n        })+\n    };\n\n    // The internal variant that generates most of the code. $name_no_R and $name_R are necessary\n    // for the impl RenderElement<SomeRenderer> for $name<SomeRenderer>: since $R does not appear\n    // in this line, we cannot condition based on $R like elsewhere, so we condition on duplicate\n    // names instead. Like this: $($name_R<SomeRenderer>)? $($name_no_R)? so only one is chosen.\n    (@impl $name:ident ($($name_no_R:ident)?) ($($name_R:ident<$R:ident>)?) => { $($variant:ident = $type:ty),+ }) => {\n        #[allow(clippy::large_enum_variant)]\n        #[derive(Debug)]\n        pub enum $name$(<$R: $crate::render_helpers::renderer::NiriRenderer>)? {\n            $($variant($type)),+\n        }\n\n        impl$(<$R: $crate::render_helpers::renderer::NiriRenderer>)? smithay::backend::renderer::element::Element for $name$(<$R>)? {\n            fn id(&self) -> &smithay::backend::renderer::element::Id {\n                match self {\n                    $($name::$variant(elem) => elem.id()),+\n                }\n            }\n\n            fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter {\n                match self {\n                    $($name::$variant(elem) => elem.current_commit()),+\n                }\n            }\n\n            fn geometry(&self, scale: smithay::utils::Scale<f64>) -> smithay::utils::Rectangle<i32, smithay::utils::Physical> {\n                match self {\n                    $($name::$variant(elem) => elem.geometry(scale)),+\n                }\n            }\n\n            fn transform(&self) -> smithay::utils::Transform {\n                match self {\n                    $($name::$variant(elem) => elem.transform()),+\n                }\n            }\n\n            fn src(&self) -> smithay::utils::Rectangle<f64, smithay::utils::Buffer> {\n                match self {\n                    $($name::$variant(elem) => elem.src()),+\n                }\n            }\n\n            fn damage_since(\n                &self,\n                scale: smithay::utils::Scale<f64>,\n                commit: Option<smithay::backend::renderer::utils::CommitCounter>,\n            ) -> smithay::backend::renderer::utils::DamageSet<i32, smithay::utils::Physical> {\n                match self {\n                    $($name::$variant(elem) => elem.damage_since(scale, commit)),+\n                }\n            }\n\n            fn opaque_regions(&self, scale: smithay::utils::Scale<f64>) -> smithay::backend::renderer::utils::OpaqueRegions<i32, smithay::utils::Physical> {\n                match self {\n                    $($name::$variant(elem) => elem.opaque_regions(scale)),+\n                }\n            }\n\n            fn alpha(&self) -> f32 {\n                match self {\n                    $($name::$variant(elem) => elem.alpha()),+\n                }\n            }\n\n            fn kind(&self) -> smithay::backend::renderer::element::Kind {\n                match self {\n                    $($name::$variant(elem) => elem.kind()),+\n                }\n            }\n        }\n\n        impl smithay::backend::renderer::element::RenderElement<smithay::backend::renderer::gles::GlesRenderer>\n            for $($name_R<smithay::backend::renderer::gles::GlesRenderer>)? $($name_no_R)?\n        {\n            fn draw(\n                &self,\n                frame: &mut smithay::backend::renderer::gles::GlesFrame<'_, '_>,\n                src: smithay::utils::Rectangle<f64, smithay::utils::Buffer>,\n                dst: smithay::utils::Rectangle<i32, smithay::utils::Physical>,\n                damage: &[smithay::utils::Rectangle<i32, smithay::utils::Physical>],\n                opaque_regions: &[smithay::utils::Rectangle<i32, smithay::utils::Physical>],\n            ) -> Result<(), smithay::backend::renderer::gles::GlesError> {\n                match self {\n                    $($name::$variant(elem) => {\n                        smithay::backend::renderer::element::RenderElement::<smithay::backend::renderer::gles::GlesRenderer>::draw(elem, frame, src, dst, damage, opaque_regions)\n                    })+\n                }\n            }\n\n            fn underlying_storage(&self, renderer: &mut smithay::backend::renderer::gles::GlesRenderer) -> Option<smithay::backend::renderer::element::UnderlyingStorage<'_>> {\n                match self {\n                    $($name::$variant(elem) => elem.underlying_storage(renderer)),+\n                }\n            }\n        }\n\n        impl<'render> smithay::backend::renderer::element::RenderElement<$crate::backend::tty::TtyRenderer<'render>>\n            for $($name_R<$crate::backend::tty::TtyRenderer<'render>>)? $($name_no_R)?\n        {\n            fn draw(\n                &self,\n                frame: &mut $crate::backend::tty::TtyFrame<'render, '_, '_>,\n                src: smithay::utils::Rectangle<f64, smithay::utils::Buffer>,\n                dst: smithay::utils::Rectangle<i32, smithay::utils::Physical>,\n                damage: &[smithay::utils::Rectangle<i32, smithay::utils::Physical>],\n                opaque_regions: &[smithay::utils::Rectangle<i32, smithay::utils::Physical>],\n            ) -> Result<(), $crate::backend::tty::TtyRendererError<'render>> {\n                match self {\n                    $($name::$variant(elem) => {\n                        smithay::backend::renderer::element::RenderElement::<$crate::backend::tty::TtyRenderer<'render>>::draw(elem, frame, src, dst, damage, opaque_regions)\n                    })+\n                }\n            }\n\n            fn underlying_storage(\n                &self,\n                renderer: &mut $crate::backend::tty::TtyRenderer<'render>,\n            ) -> Option<smithay::backend::renderer::element::UnderlyingStorage<'_>> {\n                match self {\n                    $($name::$variant(elem) => elem.underlying_storage(renderer)),+\n                }\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "src/render_helpers/renderer.rs",
    "content": "use smithay::backend::allocator::dmabuf::Dmabuf;\nuse smithay::backend::renderer::gles::{GlesFrame, GlesRenderer, GlesTexture};\nuse smithay::backend::renderer::{\n    Bind, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, RendererSuper, Texture,\n};\n\nuse crate::backend::tty::{TtyFrame, TtyRenderer};\n\n/// Trait with our main renderer requirements to save on the typing.\npub trait NiriRenderer:\n    ImportAll\n    + ImportMem\n    + ExportMem\n    + Bind<Dmabuf>\n    + Offscreen<GlesTexture>\n    + Renderer<TextureId = Self::NiriTextureId, Error = Self::NiriError>\n    + AsGlesRenderer\n{\n    // Associated types to work around the instability of associated type bounds.\n    type NiriTextureId: Texture + Clone + Send + 'static;\n    type NiriError: std::error::Error\n        + Send\n        + Sync\n        + From<<GlesRenderer as RendererSuper>::Error>\n        + 'static;\n}\n\nimpl<R> NiriRenderer for R\nwhere\n    R: ImportAll + ImportMem + ExportMem + Bind<Dmabuf> + Offscreen<GlesTexture> + AsGlesRenderer,\n    R::TextureId: Texture + Clone + Send + 'static,\n    R::Error:\n        std::error::Error + Send + Sync + From<<GlesRenderer as RendererSuper>::Error> + 'static,\n{\n    type NiriTextureId = R::TextureId;\n    type NiriError = R::Error;\n}\n\n/// Trait for getting the underlying `GlesRenderer`.\npub trait AsGlesRenderer {\n    fn as_gles_renderer(&mut self) -> &mut GlesRenderer;\n}\n\nimpl AsGlesRenderer for GlesRenderer {\n    fn as_gles_renderer(&mut self) -> &mut GlesRenderer {\n        self\n    }\n}\n\nimpl AsGlesRenderer for TtyRenderer<'_> {\n    fn as_gles_renderer(&mut self) -> &mut GlesRenderer {\n        self.as_mut()\n    }\n}\n\n/// Trait for getting the underlying `GlesFrame`.\npub trait AsGlesFrame<'frame, 'buffer>\nwhere\n    Self: 'frame,\n{\n    fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame, 'buffer>;\n}\n\nimpl<'frame, 'buffer> AsGlesFrame<'frame, 'buffer> for GlesFrame<'frame, 'buffer> {\n    fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame, 'buffer> {\n        self\n    }\n}\n\nimpl<'frame, 'buffer> AsGlesFrame<'frame, 'buffer> for TtyFrame<'_, 'frame, 'buffer> {\n    fn as_gles_frame(&mut self) -> &mut GlesFrame<'frame, 'buffer> {\n        self.as_mut()\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/resize.rs",
    "content": "use std::collections::HashMap;\nuse std::rc::Rc;\n\nuse glam::{Mat3, Vec2};\nuse niri_config::CornerRadius;\nuse smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, GlesTexture, Uniform};\nuse smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};\nuse smithay::backend::renderer::Texture as _;\nuse smithay::gpu_span_location;\nuse smithay::utils::{Buffer, Logical, Physical, Rectangle, Scale, Size, Transform};\n\nuse super::renderer::{AsGlesFrame, NiriRenderer};\nuse super::shader_element::ShaderRenderElement;\nuse super::shaders::{mat3_uniform, ProgramType, Shaders};\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\n\n#[derive(Debug)]\npub struct ResizeRenderElement(ShaderRenderElement);\n\nimpl ResizeRenderElement {\n    #[allow(clippy::too_many_arguments)]\n    pub fn new(\n        area: Rectangle<f64, Logical>,\n        scale: Scale<f64>,\n        texture_prev: (GlesTexture, Rectangle<i32, Physical>),\n        size_prev: Size<f64, Logical>,\n        texture_next: (GlesTexture, Rectangle<i32, Physical>),\n        size_next: Size<f64, Logical>,\n        progress: f32,\n        clamped_progress: f32,\n        corner_radius: CornerRadius,\n        clip_to_geometry: bool,\n        result_alpha: f32,\n    ) -> Self {\n        let curr_geo = area;\n\n        let (texture_prev, tex_prev_geo) = texture_prev;\n        let (texture_next, tex_next_geo) = texture_next;\n\n        let scale_prev = area.size / size_prev;\n        let scale_next = area.size / size_next;\n\n        // Compute the area necessary to fit a crossfade.\n        let tex_prev_geo_scaled = tex_prev_geo.to_f64().upscale(scale_prev);\n        let tex_next_geo_scaled = tex_next_geo.to_f64().upscale(scale_next);\n        let combined_geo = tex_prev_geo_scaled.merge(tex_next_geo_scaled).to_i32_up();\n\n        let area = Rectangle::new(\n            area.loc + combined_geo.loc.to_logical(scale),\n            combined_geo.size.to_logical(scale),\n        );\n\n        // Convert Smithay types into glam types.\n        let area_loc = Vec2::new(area.loc.x as f32, area.loc.y as f32);\n        let area_size = Vec2::new(area.size.w as f32, area.size.h as f32);\n\n        let curr_geo_loc = Vec2::new(curr_geo.loc.x as f32, curr_geo.loc.y as f32);\n        let curr_geo_size = Vec2::new(curr_geo.size.w as f32, curr_geo.size.h as f32);\n\n        let tex_prev_geo_loc = Vec2::new(tex_prev_geo.loc.x as f32, tex_prev_geo.loc.y as f32);\n        let tex_prev_size = Vec2::new(texture_prev.width() as f32, texture_prev.height() as f32);\n\n        let tex_next_geo_loc = Vec2::new(tex_next_geo.loc.x as f32, tex_next_geo.loc.y as f32);\n        let tex_next_size = Vec2::new(texture_next.width() as f32, texture_next.height() as f32);\n\n        let size_prev = Vec2::new(size_prev.w as f32, size_prev.h as f32);\n        let size_next = Vec2::new(size_next.w as f32, size_next.h as f32);\n\n        let scale = Vec2::new(scale.x as f32, scale.y as f32);\n\n        // Compute the transformation matrices.\n        let input_to_curr_geo = Mat3::from_scale(area_size / curr_geo_size)\n            * Mat3::from_translation((area_loc - curr_geo_loc) / area_size);\n\n        let curr_geo_to_prev_geo = Mat3::from_scale(curr_geo_size / size_prev);\n        let curr_geo_to_next_geo = Mat3::from_scale(curr_geo_size / size_next);\n\n        let geo_to_tex_prev = Mat3::from_translation(-tex_prev_geo_loc / tex_prev_size)\n            * Mat3::from_scale(size_prev / tex_prev_size * scale);\n        let geo_to_tex_next = Mat3::from_translation(-tex_next_geo_loc / tex_next_size)\n            * Mat3::from_scale(size_next / tex_next_size * scale);\n\n        let corner_radius = corner_radius.fit_to(curr_geo_size.x, curr_geo_size.y);\n        let clip_to_geometry = if clip_to_geometry { 1. } else { 0. };\n\n        // Create the shader.\n        Self(\n            ShaderRenderElement::new(\n                ProgramType::Resize,\n                area.size,\n                None,\n                scale.x,\n                result_alpha,\n                Rc::new([\n                    mat3_uniform(\"niri_input_to_curr_geo\", input_to_curr_geo),\n                    mat3_uniform(\"niri_curr_geo_to_prev_geo\", curr_geo_to_prev_geo),\n                    mat3_uniform(\"niri_curr_geo_to_next_geo\", curr_geo_to_next_geo),\n                    Uniform::new(\"niri_curr_geo_size\", curr_geo_size.to_array()),\n                    mat3_uniform(\"niri_geo_to_tex_prev\", geo_to_tex_prev),\n                    mat3_uniform(\"niri_geo_to_tex_next\", geo_to_tex_next),\n                    Uniform::new(\"niri_progress\", progress),\n                    Uniform::new(\"niri_clamped_progress\", clamped_progress),\n                    Uniform::new(\"niri_corner_radius\", <[f32; 4]>::from(corner_radius)),\n                    Uniform::new(\"niri_clip_to_geometry\", clip_to_geometry),\n                ]),\n                HashMap::from([\n                    (String::from(\"niri_tex_prev\"), texture_prev),\n                    (String::from(\"niri_tex_next\"), texture_next),\n                ]),\n                Kind::Unspecified,\n            )\n            .with_location(area.loc),\n        )\n    }\n\n    pub fn has_shader(renderer: &mut impl NiriRenderer) -> bool {\n        Shaders::get(renderer)\n            .program(ProgramType::Resize)\n            .is_some()\n    }\n}\n\nimpl Element for ResizeRenderElement {\n    fn id(&self) -> &Id {\n        self.0.id()\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.0.current_commit()\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.0.geometry(scale)\n    }\n\n    fn transform(&self) -> Transform {\n        self.0.transform()\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        self.0.src()\n    }\n\n    fn damage_since(\n        &self,\n        scale: Scale<f64>,\n        commit: Option<CommitCounter>,\n    ) -> DamageSet<i32, Physical> {\n        self.0.damage_since(scale, commit)\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        self.0.opaque_regions(scale)\n    }\n\n    fn alpha(&self) -> f32 {\n        self.0.alpha()\n    }\n\n    fn kind(&self) -> Kind {\n        self.0.kind()\n    }\n}\n\nimpl RenderElement<GlesRenderer> for ResizeRenderElement {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        let _span = tracy_client::span!(\"ResizeRenderElement::draw\");\n        frame.with_gpu_span(gpu_span_location!(\"ResizeRenderElement::draw\"), |frame| {\n            RenderElement::<GlesRenderer>::draw(&self.0, frame, src, dst, damage, opaque_regions)\n        })\n    }\n\n    fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        self.0.underlying_storage(renderer)\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>> for ResizeRenderElement {\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'_, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        let frame = frame.as_gles_frame();\n        RenderElement::<GlesRenderer>::draw(self, frame, src, dst, damage, opaque_regions)?;\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        self.0.underlying_storage(renderer)\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/resources.rs",
    "content": "use std::cell::RefCell;\nuse std::rc::Rc;\n\nuse smithay::backend::renderer::gles::{ffi, Capability, GlesError, GlesFrame, GlesRenderer};\n\npub struct Resources {\n    pub vertices: Vec<f32>,\n    pub vbos: [ffi::types::GLuint; 2],\n}\n\nstatic INSTANCED_VERTS: [ffi::types::GLfloat; 8] = [\n    1.0, 0.0, // top right\n    0.0, 0.0, // top left\n    1.0, 1.0, // bottom right\n    0.0, 1.0, // bottom left\n];\n\n/// Vertices for rendering individual triangles.\nconst MAX_RECTS_PER_DRAW: usize = 10;\nconst TRIANGLE_VERTS: [ffi::types::GLfloat; 12 * MAX_RECTS_PER_DRAW] = triangle_verts();\nconst fn triangle_verts() -> [ffi::types::GLfloat; 12 * MAX_RECTS_PER_DRAW] {\n    let mut verts = [0.; 12 * MAX_RECTS_PER_DRAW];\n    let mut i = 0;\n    loop {\n        // Top Left.\n        verts[i * 12] = 0.0;\n        verts[i * 12 + 1] = 0.0;\n\n        // Bottom left.\n        verts[i * 12 + 2] = 0.0;\n        verts[i * 12 + 3] = 1.0;\n\n        // Bottom right.\n        verts[i * 12 + 4] = 1.0;\n        verts[i * 12 + 5] = 1.0;\n\n        // Top left.\n        verts[i * 12 + 6] = 0.0;\n        verts[i * 12 + 7] = 0.0;\n\n        // Bottom right.\n        verts[i * 12 + 8] = 1.0;\n        verts[i * 12 + 9] = 1.0;\n\n        // Top right.\n        verts[i * 12 + 10] = 1.0;\n        verts[i * 12 + 11] = 0.0;\n\n        i += 1;\n        if i == MAX_RECTS_PER_DRAW {\n            break;\n        }\n    }\n    verts\n}\n\nimpl Resources {\n    fn create(renderer: &mut GlesRenderer) -> Result<Self, GlesError> {\n        let _span = tracy_client::span!(\"Resources::init\");\n\n        let supports_instancing = renderer.capabilities().contains(&Capability::Instancing);\n        renderer.with_context(|gl| unsafe {\n            let vertices: &[ffi::types::GLfloat] = if supports_instancing {\n                &INSTANCED_VERTS\n            } else {\n                &TRIANGLE_VERTS\n            };\n\n            let mut vbos = [0; 2];\n            gl.GenBuffers(vbos.len() as i32, vbos.as_mut_ptr());\n            gl.BindBuffer(ffi::ARRAY_BUFFER, vbos[0]);\n            gl.BufferData(\n                ffi::ARRAY_BUFFER,\n                std::mem::size_of_val(vertices) as isize,\n                vertices.as_ptr() as *const _,\n                ffi::STATIC_DRAW,\n            );\n\n            gl.BindBuffer(ffi::ARRAY_BUFFER, 0);\n\n            Self {\n                vertices: vec![],\n                vbos,\n            }\n        })\n    }\n\n    pub fn get(frame: &mut GlesFrame) -> Option<Rc<RefCell<Self>>> {\n        let data = frame.egl_context().user_data();\n        data.get().cloned()\n    }\n}\n\npub fn init(renderer: &mut GlesRenderer) {\n    match Resources::create(renderer) {\n        Ok(resources) => {\n            let data = renderer.egl_context().user_data();\n            if !data.insert_if_missing(|| Rc::new(RefCell::new(resources))) {\n                error!(\"resources were already initialized\");\n            }\n        }\n        Err(err) => {\n            warn!(\"error creating resources for rendering: {err:?}\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/shader_element.rs",
    "content": "use std::collections::HashMap;\nuse std::ffi::CString;\nuse std::rc::Rc;\n\nuse glam::{Mat3, Vec2};\nuse smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::{\n    ffi, link_program, Capability, GlesError, GlesFrame, GlesRenderer, GlesTexture, Uniform,\n    UniformDesc, UniformName,\n};\nuse smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};\nuse smithay::backend::renderer::DebugFlags;\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size};\n\nuse super::renderer::AsGlesFrame;\nuse super::resources::Resources;\nuse super::shaders::{ProgramType, Shaders};\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\n\n/// Renders a shader with optional texture input, on the primary GPU.\n#[derive(Debug, Clone)]\npub struct ShaderRenderElement {\n    program: ProgramType,\n    id: Id,\n    commit_counter: CommitCounter,\n    area: Rectangle<f64, Logical>,\n    opaque_regions: Vec<Rectangle<f64, Logical>>,\n    // Should only be used for visual improvements, i.e. corner radius anti-aliasing.\n    scale: f32,\n    alpha: f32,\n    additional_uniforms: Rc<[Uniform<'static>]>,\n    textures: HashMap<String, GlesTexture>,\n    kind: Kind,\n}\n\n#[derive(Debug, Clone)]\npub struct ShaderProgram(Rc<ShaderProgramInner>);\n\n#[derive(Debug)]\nstruct ShaderProgramInner {\n    normal: ShaderProgramInternal,\n    debug: ShaderProgramInternal,\n    uniform_tint: ffi::types::GLint,\n}\n\n#[derive(Debug)]\nstruct ShaderProgramInternal {\n    program: ffi::types::GLuint,\n    uniform_tex_matrix: ffi::types::GLint,\n    uniform_matrix: ffi::types::GLint,\n    uniform_size: ffi::types::GLint,\n    uniform_scale: ffi::types::GLint,\n    uniform_alpha: ffi::types::GLint,\n    attrib_vert: ffi::types::GLint,\n    attrib_vert_position: ffi::types::GLint,\n    additional_uniforms: HashMap<String, UniformDesc>,\n    texture_uniforms: HashMap<String, ffi::types::GLint>,\n}\n\nimpl PartialEq for ShaderProgram {\n    fn eq(&self, other: &Self) -> bool {\n        Rc::ptr_eq(&self.0, &other.0)\n    }\n}\n\nunsafe fn compile_program(\n    gl: &ffi::Gles2,\n    src: &str,\n    additional_uniforms: &[UniformName<'_>],\n    texture_uniforms: &[&str],\n    // destruction_callback_sender: Sender<CleanupResource>,\n) -> Result<ShaderProgram, GlesError> {\n    let shader = format!(\"#version 100\\n{src}\");\n    let program = unsafe { link_program(gl, include_str!(\"shaders/texture.vert\"), &shader)? };\n    let debug_shader = format!(\"#version 100\\n#define DEBUG_FLAGS\\n{src}\");\n    let debug_program =\n        unsafe { link_program(gl, include_str!(\"shaders/texture.vert\"), &debug_shader)? };\n\n    let vert = c\"vert\";\n    let vert_position = c\"vert_position\";\n    let matrix = c\"matrix\";\n    let tex_matrix = c\"tex_matrix\";\n    let size = c\"niri_size\";\n    let scale = c\"niri_scale\";\n    let alpha = c\"niri_alpha\";\n    let tint = c\"niri_tint\";\n\n    Ok(ShaderProgram(Rc::new(ShaderProgramInner {\n        normal: ShaderProgramInternal {\n            program,\n            uniform_matrix: gl.GetUniformLocation(program, matrix.as_ptr()),\n            uniform_tex_matrix: gl.GetUniformLocation(program, tex_matrix.as_ptr()),\n            uniform_size: gl.GetUniformLocation(program, size.as_ptr()),\n            uniform_scale: gl.GetUniformLocation(program, scale.as_ptr()),\n            uniform_alpha: gl.GetUniformLocation(program, alpha.as_ptr()),\n            attrib_vert: gl.GetAttribLocation(program, vert.as_ptr()),\n            attrib_vert_position: gl.GetAttribLocation(program, vert_position.as_ptr()),\n            additional_uniforms: additional_uniforms\n                .iter()\n                .map(|uniform| {\n                    let name =\n                        CString::new(uniform.name.as_bytes()).expect(\"Interior null in name\");\n                    let location = gl.GetUniformLocation(program, name.as_ptr());\n                    (\n                        uniform.name.clone().into_owned(),\n                        UniformDesc {\n                            location,\n                            type_: uniform.type_,\n                        },\n                    )\n                })\n                .collect(),\n            texture_uniforms: texture_uniforms\n                .iter()\n                .map(|name_| {\n                    let name = CString::new(name_.as_bytes()).expect(\"Interior null in name\");\n                    let location = gl.GetUniformLocation(program, name.as_ptr());\n                    (name_.to_string(), location)\n                })\n                .collect(),\n        },\n        debug: ShaderProgramInternal {\n            program: debug_program,\n            uniform_matrix: gl.GetUniformLocation(debug_program, matrix.as_ptr()),\n            uniform_tex_matrix: gl.GetUniformLocation(debug_program, tex_matrix.as_ptr()),\n            uniform_size: gl.GetUniformLocation(debug_program, size.as_ptr()),\n            uniform_scale: gl.GetUniformLocation(debug_program, scale.as_ptr()),\n            uniform_alpha: gl.GetUniformLocation(debug_program, alpha.as_ptr()),\n            attrib_vert: gl.GetAttribLocation(debug_program, vert.as_ptr()),\n            attrib_vert_position: gl.GetAttribLocation(debug_program, vert_position.as_ptr()),\n            additional_uniforms: additional_uniforms\n                .iter()\n                .map(|uniform| {\n                    let name =\n                        CString::new(uniform.name.as_bytes()).expect(\"Interior null in name\");\n                    let location = gl.GetUniformLocation(debug_program, name.as_ptr());\n                    (\n                        uniform.name.clone().into_owned(),\n                        UniformDesc {\n                            location,\n                            type_: uniform.type_,\n                        },\n                    )\n                })\n                .collect(),\n            texture_uniforms: texture_uniforms\n                .iter()\n                .map(|name_| {\n                    let name = CString::new(name_.as_bytes()).expect(\"Interior null in name\");\n                    let location = gl.GetUniformLocation(debug_program, name.as_ptr());\n                    (name_.to_string(), location)\n                })\n                .collect(),\n        },\n        uniform_tint: gl.GetUniformLocation(debug_program, tint.as_ptr()),\n    })))\n}\n\nimpl ShaderProgram {\n    pub fn compile(\n        renderer: &mut GlesRenderer,\n        src: &str,\n        additional_uniforms: &[UniformName<'_>],\n        texture_uniforms: &[&str],\n    ) -> Result<Self, GlesError> {\n        renderer.with_context(move |gl| unsafe {\n            compile_program(gl, src, additional_uniforms, texture_uniforms)\n        })?\n    }\n\n    pub fn destroy(self, renderer: &mut GlesRenderer) -> Result<(), GlesError> {\n        renderer.with_context(move |gl| unsafe {\n            gl.DeleteProgram(self.0.normal.program);\n            gl.DeleteProgram(self.0.debug.program);\n        })\n    }\n}\n\nimpl ShaderRenderElement {\n    #[allow(clippy::too_many_arguments)]\n    pub fn new(\n        program: ProgramType,\n        size: Size<f64, Logical>,\n        opaque_regions: Option<Vec<Rectangle<f64, Logical>>>,\n        // Should only be used for visual improvements, i.e. corner radius anti-aliasing.\n        scale: f32,\n        alpha: f32,\n        additional_uniforms: Rc<[Uniform<'static>]>,\n        textures: HashMap<String, GlesTexture>,\n        kind: Kind,\n    ) -> Self {\n        Self {\n            program,\n            id: Id::new(),\n            commit_counter: CommitCounter::default(),\n            area: Rectangle::from_size(size),\n            opaque_regions: opaque_regions.unwrap_or_default(),\n            scale,\n            alpha,\n            additional_uniforms,\n            textures,\n            kind,\n        }\n    }\n\n    pub fn empty(program: ProgramType, kind: Kind) -> Self {\n        Self {\n            program,\n            id: Id::new(),\n            commit_counter: CommitCounter::default(),\n            area: Rectangle::default(),\n            opaque_regions: vec![],\n            scale: 1.,\n            alpha: 1.,\n            additional_uniforms: Rc::new([]),\n            textures: HashMap::new(),\n            kind,\n        }\n    }\n\n    pub fn damage_all(&mut self) {\n        self.commit_counter.increment();\n    }\n\n    pub fn update(\n        &mut self,\n        size: Size<f64, Logical>,\n        opaque_regions: Option<Vec<Rectangle<f64, Logical>>>,\n        scale: f32,\n        alpha: f32,\n        uniforms: Rc<[Uniform<'static>]>,\n        textures: HashMap<String, GlesTexture>,\n    ) {\n        self.area.size = size;\n        self.opaque_regions = opaque_regions.unwrap_or_default();\n        self.scale = scale;\n        self.alpha = alpha;\n        self.additional_uniforms = uniforms;\n        self.textures = textures;\n\n        self.commit_counter.increment();\n    }\n\n    pub fn with_location(mut self, location: Point<f64, Logical>) -> Self {\n        self.area.loc = location;\n        self\n    }\n\n    pub fn with_alpha(mut self, alpha: f32) -> Self {\n        self.alpha = alpha;\n        self\n    }\n}\n\nimpl Element for ShaderRenderElement {\n    fn id(&self) -> &Id {\n        &self.id\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.commit_counter\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        Rectangle::from_size(Size::from((1., 1.)))\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.area.to_physical_precise_round(scale)\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        self.opaque_regions\n            .iter()\n            .map(|region| region.to_physical_precise_down(scale))\n            .collect()\n    }\n\n    fn alpha(&self) -> f32 {\n        self.alpha\n    }\n\n    fn kind(&self) -> Kind {\n        self.kind\n    }\n}\n\nimpl RenderElement<GlesRenderer> for ShaderRenderElement {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dest: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        _opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        let _span = tracy_client::span!(\"ShaderRenderElement::draw\");\n\n        let frame = frame.as_gles_frame();\n\n        let Some(shader) = Shaders::get_from_frame(frame).program(self.program) else {\n            return Ok(());\n        };\n\n        let Some(resources) = Resources::get(frame) else {\n            return Ok(());\n        };\n        let mut resources = resources.borrow_mut();\n\n        let supports_instancing = frame.capabilities().contains(&Capability::Instancing);\n\n        // prepare the vertices\n        resources.vertices.clear();\n        if supports_instancing {\n            resources.vertices.extend(damage.iter().flat_map(|rect| {\n                let dest_size = dest.size;\n\n                let rect_constrained_loc = rect\n                    .loc\n                    .constrain(Rectangle::from_extremities((0, 0), dest_size.to_point()));\n                let rect_clamped_size = rect.size.clamp(\n                    (0, 0),\n                    (dest_size.to_point() - rect_constrained_loc).to_size(),\n                );\n\n                let rect = Rectangle::new(rect_constrained_loc, rect_clamped_size);\n                [\n                    rect.loc.x as f32,\n                    rect.loc.y as f32,\n                    rect.size.w as f32,\n                    rect.size.h as f32,\n                ]\n            }));\n        } else {\n            resources.vertices.extend(damage.iter().flat_map(|rect| {\n                let dest_size = dest.size;\n\n                let rect_constrained_loc = rect\n                    .loc\n                    .constrain(Rectangle::from_extremities((0, 0), dest_size.to_point()));\n                let rect_clamped_size = rect.size.clamp(\n                    (0, 0),\n                    (dest_size.to_point() - rect_constrained_loc).to_size(),\n                );\n\n                let rect = Rectangle::new(rect_constrained_loc, rect_clamped_size);\n                // Add the 4 f32s per damage rectangle for each of the 6 vertices.\n                (0..6).flat_map(move |_| {\n                    [\n                        rect.loc.x as f32,\n                        rect.loc.y as f32,\n                        rect.size.w as f32,\n                        rect.size.h as f32,\n                    ]\n                })\n            }));\n        }\n\n        if resources.vertices.is_empty() {\n            return Ok(());\n        }\n\n        // dest position and scale\n        let mut matrix = Mat3::from_translation(Vec2::new(dest.loc.x as f32, dest.loc.y as f32));\n\n        let scale = src.size.to_f64() / dest.size.to_f64();\n        let tex_matrix = Mat3::from_scale(Vec2::new(scale.x as f32, scale.y as f32));\n        let tex_matrix =\n            Mat3::from_translation(Vec2::new(src.loc.x as f32, src.loc.y as f32)) * tex_matrix;\n\n        //apply output transformation\n        matrix = Mat3::from_cols_array(frame.projection()) * matrix;\n\n        let has_debug = !frame.debug_flags().is_empty();\n        let has_tint = frame.debug_flags().contains(DebugFlags::TINT);\n\n        // render\n        let span_loc = smithay::gpu_span_location!(\"draw shader\");\n        frame.with_profiled_context(span_loc, move |gl| -> Result<(), GlesError> {\n            let program = if has_debug {\n                &shader.0.debug\n            } else {\n                &shader.0.normal\n            };\n\n            unsafe {\n                for (i, texture) in self.textures.values().enumerate() {\n                    gl.ActiveTexture(ffi::TEXTURE0 + i as u32);\n                    gl.BindTexture(ffi::TEXTURE_2D, texture.tex_id());\n                    gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32);\n                    gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_MAG_FILTER, ffi::LINEAR as i32);\n                    gl.TexParameteri(\n                        ffi::TEXTURE_2D,\n                        ffi::TEXTURE_WRAP_S,\n                        ffi::CLAMP_TO_BORDER as i32,\n                    );\n                    gl.TexParameteri(\n                        ffi::TEXTURE_2D,\n                        ffi::TEXTURE_WRAP_T,\n                        ffi::CLAMP_TO_BORDER as i32,\n                    );\n                }\n\n                gl.UseProgram(program.program);\n\n                for (i, name) in self.textures.keys().enumerate() {\n                    gl.Uniform1i(program.texture_uniforms[name], i as i32);\n                }\n\n                gl.UniformMatrix3fv(\n                    program.uniform_matrix,\n                    1,\n                    ffi::FALSE,\n                    matrix.as_ref().as_ptr(),\n                );\n                gl.UniformMatrix3fv(\n                    program.uniform_tex_matrix,\n                    1,\n                    ffi::FALSE,\n                    tex_matrix.as_ref().as_ptr(),\n                );\n                gl.Uniform2f(program.uniform_size, dest.size.w as f32, dest.size.h as f32);\n                gl.Uniform1f(program.uniform_scale, self.scale);\n                gl.Uniform1f(program.uniform_alpha, self.alpha);\n\n                let tint = if has_tint { 1.0f32 } else { 0.0f32 };\n                if has_debug {\n                    gl.Uniform1f(shader.0.uniform_tint, tint);\n                }\n\n                for uniform in &*self.additional_uniforms {\n                    let desc =\n                        program\n                            .additional_uniforms\n                            .get(&*uniform.name)\n                            .ok_or_else(|| {\n                                GlesError::UnknownUniform(uniform.name.clone().into_owned())\n                            })?;\n                    uniform.value.set(gl, desc)?;\n                }\n\n                gl.EnableVertexAttribArray(program.attrib_vert as u32);\n                gl.BindBuffer(ffi::ARRAY_BUFFER, resources.vbos[0]);\n                gl.VertexAttribPointer(\n                    program.attrib_vert as u32,\n                    2,\n                    ffi::FLOAT,\n                    ffi::FALSE,\n                    0,\n                    std::ptr::null(),\n                );\n\n                // vert_position\n                gl.EnableVertexAttribArray(program.attrib_vert_position as u32);\n                gl.BindBuffer(ffi::ARRAY_BUFFER, resources.vbos[1]);\n                gl.BufferData(\n                    ffi::ARRAY_BUFFER,\n                    (std::mem::size_of::<ffi::types::GLfloat>() * resources.vertices.len())\n                        as isize,\n                    resources.vertices.as_ptr() as *const _,\n                    ffi::STREAM_DRAW,\n                );\n\n                gl.VertexAttribPointer(\n                    program.attrib_vert_position as u32,\n                    4,\n                    ffi::FLOAT,\n                    ffi::FALSE,\n                    0,\n                    std::ptr::null(),\n                );\n\n                let damage_len = damage.len() as i32;\n                if supports_instancing {\n                    gl.VertexAttribDivisor(program.attrib_vert as u32, 0);\n                    gl.VertexAttribDivisor(program.attrib_vert_position as u32, 1);\n                    gl.DrawArraysInstanced(ffi::TRIANGLE_STRIP, 0, 4, damage_len);\n                } else {\n                    // When we have more than 10 rectangles, draw them in batches of 10.\n                    for i in 0..(damage_len - 1) / 10 {\n                        gl.DrawArrays(ffi::TRIANGLES, 0, 6);\n\n                        // Set damage pointer to the next 10 rectangles.\n                        let offset =\n                            (i + 1) as usize * 6 * 4 * std::mem::size_of::<ffi::types::GLfloat>();\n                        gl.VertexAttribPointer(\n                            program.attrib_vert_position as u32,\n                            4,\n                            ffi::FLOAT,\n                            ffi::FALSE,\n                            0,\n                            offset as *const _,\n                        );\n                    }\n\n                    // Draw the up to 10 remaining rectangles.\n                    let count = ((damage_len - 1) % 10 + 1) * 6;\n                    gl.DrawArrays(ffi::TRIANGLES, 0, count);\n                }\n\n                gl.BindBuffer(ffi::ARRAY_BUFFER, 0);\n                gl.BindTexture(ffi::TEXTURE_2D, 0);\n                gl.ActiveTexture(ffi::TEXTURE0);\n                gl.BindTexture(ffi::TEXTURE_2D, 0);\n                gl.DisableVertexAttribArray(program.attrib_vert as u32);\n                gl.DisableVertexAttribArray(program.attrib_vert_position as u32);\n            }\n\n            Ok(())\n        })??;\n\n        Ok(())\n    }\n\n    fn underlying_storage(&self, _renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>> for ShaderRenderElement {\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'_, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        let frame = frame.as_gles_frame();\n\n        RenderElement::<GlesRenderer>::draw(self, frame, src, dst, damage, opaque_regions)?;\n\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        _renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        // If scanout for things other than Wayland buffers is implemented, this will need to take\n        // the target GPU into account.\n        None\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/border.frag",
    "content": "precision highp float;\n\n#if defined(DEBUG_FLAGS)\nuniform float niri_tint;\n#endif\n\nuniform float niri_alpha;\nuniform float niri_scale;\n\nuniform vec2 niri_size;\nvarying vec2 niri_v_coords;\n\nuniform float colorspace;\nuniform float hue_interpolation;\nuniform vec4 color_from;\nuniform vec4 color_to;\nuniform vec2 grad_offset;\nuniform float grad_width;\nuniform vec2 grad_vec;\n\nuniform mat3 input_to_geo;\nuniform vec2 geo_size;\nuniform vec4 outer_radius;\nuniform float border_width;\n\nvec4 premul_rect(vec4 color) {\n    color.rgb *= color.a;\n    return color;\n}\n\nvec4 premul_lch(vec4 color) {\n    color.xy *= color.a;\n    return color;\n}\n\nvec4 unpremul_rect(vec4 color) {\n    if (color.a == 0.0)\n        return color;\n\n    color.rgb /= color.a;\n    return color;\n}\n\nvec4 unpremul_lch(vec4 color) {\n    if (color.a == 0.0)\n        return color;\n\n    color.xy /= color.a;\n    return color;\n}\n\nvec4 premul_mix_unpremul_rect(vec4 color1, vec4 color2, float ratio) {\n    vec4 mixed = mix(premul_rect(color1), premul_rect(color2), ratio);\n    return unpremul_rect(mixed);\n}\n\nvec4 premul_mix_unpremul_lch(vec4 color1, vec4 color2, float ratio) {\n    vec4 mixed = mix(premul_lch(color1), premul_lch(color2), ratio);\n    return unpremul_lch(mixed);\n}\n\nvec3 srgb_to_linear(vec3 color) {\n    return pow(color, vec3(2.2));\n}\n\nvec3 linear_to_srgb(vec3 color) {\n    return pow(color, vec3(1.0 / 2.2));\n}\n\nvec3 lab_to_lch(vec3 color) {\n    float c = sqrt(pow(color.y, 2.0) + pow(color.z, 2.0));\n    float h = degrees(atan(color.z, color.y)) ;\n    h += h <= 0.0 ?\n        360.0 :\n        0.0 ;\n    return vec3(\n        color.x,\n        c,\n        h\n    );\n}\n\nvec3 lch_to_lab(vec3 color) {\n    float a = color.y * clamp(cos(radians(color.z)), -1.0, 1.0);\n    float b = color.y * clamp(sin(radians(color.z)), -1.0, 1.0);\n    return vec3(\n        color.x,\n        a,\n        b\n    );\n}\n\nvec3 linear_to_oklab(vec3 color){\n    mat3 rgb_to_lms = mat3(\n        vec3(0.4122214708, 0.5363325363, 0.0514459929),\n        vec3(0.2119034982, 0.6806995451, 0.1073969566),\n        vec3(0.0883024619, 0.2817188376, 0.6299787005)\n    );\n    mat3 lms_to_oklab = mat3(\n        vec3(0.2104542553, 0.7936177850, -0.0040720468),\n        vec3(1.9779984951, -2.4285922050, 0.4505937099),\n        vec3(0.0259040371, 0.7827717662, -0.8086757660)\n    );\n    vec3 lms = color * rgb_to_lms;\n    lms = pow(lms, vec3(1.0 / 3.0));\n    return lms * lms_to_oklab;\n}\n\nvec3 oklab_to_linear(vec3 color){\n    mat3 oklab_to_lms = mat3(\n        vec3(1.0, 0.3963377774, 0.2158037573),\n        vec3(1.0, -0.1055613458, -0.0638541728),\n        vec3(1.0, -0.0894841775, -1.2914855480)\n    );\n    mat3 lms_to_rgb = mat3(\n        vec3(4.0767416621, -3.3077115913, 0.2309699292),\n        vec3(-1.2684380046, 2.6097574011, -0.3413193965),\n        vec3(-0.0041960863, -0.7034186147, 1.7076147010)\n    );\n    vec3 lms = color * oklab_to_lms;\n    lms = pow(lms, vec3(3.0));\n    return lms * lms_to_rgb;\n}\n\nvec4 color_mix(vec4 color1, vec4 color2, float color_ratio) {\n    vec4 color_out;\n\n    // srgb\n    if (colorspace == 0.0) {\n        return mix(premul_rect(color1), premul_rect(color2), color_ratio);\n    }\n\n    color1.rgb = srgb_to_linear(color1.rgb);\n    color2.rgb = srgb_to_linear(color2.rgb);\n\n    // srgb-linear\n    if (colorspace == 1.0) {\n        color_out = premul_mix_unpremul_rect(color1, color2, color_ratio);\n    // oklab\n    } else if (colorspace == 2.0) {\n        color1.xyz = linear_to_oklab(color1.rgb);\n        color2.xyz = linear_to_oklab(color2.rgb);\n        color_out = premul_mix_unpremul_rect(color1, color2, color_ratio);\n        color_out.rgb = oklab_to_linear(color_out.xyz);\n    // oklch\n    } else if (colorspace == 3.0) {\n        color1.xyz = lab_to_lch(linear_to_oklab(color1.rgb));\n        color2.xyz = lab_to_lch(linear_to_oklab(color2.rgb));\n        color_out = premul_mix_unpremul_lch(color1, color2, color_ratio);\n\n        float min_hue = min(color1.z, color2.z);\n        float max_hue = max(color1.z, color2.z);\n        float path_direct_distance = (max_hue - min_hue) * color_ratio;\n        float path_mod_distance = (360.0 - max_hue + min_hue) * color_ratio;\n\n        float path_mod =\n            color1.z == min_hue ?\n                mod(color1.z - path_mod_distance, 360.0) :\n                mod(color1.z + path_mod_distance, 360.0) ;\n        float path_direct =\n            color1.z == min_hue ?\n                color1.z + path_direct_distance :\n                color1.z - path_direct_distance ;\n\n        // shorter\n        if (hue_interpolation == 0.0) {\n            color_out.z =\n                max_hue - min_hue > 360.0 - max_hue + min_hue ?\n                    path_mod :\n                    path_direct ;\n        // longer\n        } else if (hue_interpolation == 1.0) {\n            color_out.z =\n                max_hue - min_hue <= 360.0 - max_hue + min_hue ?\n                    path_mod :\n                    path_direct ;\n        // increasing\n        } else if (hue_interpolation == 2.0) {\n            color_out.z =\n                color1.z > color2.z ?\n                    path_mod :\n                    path_direct ;\n        // decreasing\n        } else if (hue_interpolation == 3.0) {\n            color_out.z =\n                color1.z <= color2.z ?\n                    path_mod :\n                    path_direct ;\n        }\n        color_out.rgb = clamp(oklab_to_linear(lch_to_lab(color_out.xyz)), 0.0, 1.0);\n    }\n\n    return premul_rect(vec4(linear_to_srgb(color_out.rgb), color_out.a));\n}\n\nvec4 gradient_color(vec2 coords) {\n    coords = coords + grad_offset;\n\n    if ((grad_vec.x < 0.0 && 0.0 <= grad_vec.y) || (0.0 <= grad_vec.x && grad_vec.y < 0.0))\n        coords.x -= grad_width;\n\n    float frac = dot(coords, grad_vec) / dot(grad_vec, grad_vec);\n\n    if (grad_vec.y < 0.0)\n        frac += 1.0;\n\n    frac = clamp(frac, 0.0, 1.0);\n    return color_mix(color_from, color_to, frac);\n}\n\nfloat rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) {\n    vec2 center;\n    float radius;\n\n    if (coords.x < corner_radius.x && coords.y < corner_radius.x) {\n        radius = corner_radius.x;\n        center = vec2(radius, radius);\n    } else if (size.x - corner_radius.y < coords.x && coords.y < corner_radius.y) {\n        radius = corner_radius.y;\n        center = vec2(size.x - radius, radius);\n    } else if (size.x - corner_radius.z < coords.x && size.y - corner_radius.z < coords.y) {\n        radius = corner_radius.z;\n        center = vec2(size.x - radius, size.y - radius);\n    } else if (coords.x < corner_radius.w && size.y - corner_radius.w < coords.y) {\n        radius = corner_radius.w;\n        center = vec2(radius, size.y - radius);\n    } else {\n        return 1.0;\n    }\n\n    float dist = distance(coords, center);\n    float half_px = 0.5 / niri_scale;\n    return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);\n}\n\nvoid main() {\n    vec3 coords_geo = input_to_geo * vec3(niri_v_coords, 1.0);\n    vec4 color = gradient_color(coords_geo.xy);\n    color = color * rounding_alpha(coords_geo.xy, geo_size, outer_radius);\n\n    if (border_width > 0.0) {\n        coords_geo -= vec3(border_width);\n        vec2 inner_geo_size = geo_size - vec2(border_width * 2.0);\n        if (0.0 <= coords_geo.x && coords_geo.x <= inner_geo_size.x\n                && 0.0 <= coords_geo.y && coords_geo.y <= inner_geo_size.y)\n        {\n            vec4 inner_radius = max(outer_radius - vec4(border_width), 0.0);\n            color = color * (1.0 - rounding_alpha(coords_geo.xy, inner_geo_size, inner_radius));\n        }\n    }\n\n    color = color * niri_alpha;\n\n#if defined(DEBUG_FLAGS)\n    if (niri_tint == 1.0)\n        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;\n#endif\n\n    gl_FragColor = color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/clipped_surface.frag",
    "content": "#version 100\n\n//_DEFINES_\n\n#if defined(EXTERNAL)\n#extension GL_OES_EGL_image_external : require\n#endif\n\nprecision highp float;\n#if defined(EXTERNAL)\nuniform samplerExternalOES tex;\n#else\nuniform sampler2D tex;\n#endif\n\nuniform float alpha;\nvarying vec2 v_coords;\n\n#if defined(DEBUG_FLAGS)\nuniform float tint;\n#endif\n\nuniform float niri_scale;\n\nuniform vec2 geo_size;\nuniform vec4 corner_radius;\nuniform mat3 input_to_geo;\n\nfloat rounding_alpha(vec2 coords, vec2 size) {\n    vec2 center;\n    float radius;\n\n    if (coords.x < corner_radius.x && coords.y < corner_radius.x) {\n        radius = corner_radius.x;\n        center = vec2(radius, radius);\n    } else if (size.x - corner_radius.y < coords.x && coords.y < corner_radius.y) {\n        radius = corner_radius.y;\n        center = vec2(size.x - radius, radius);\n    } else if (size.x - corner_radius.z < coords.x && size.y - corner_radius.z < coords.y) {\n        radius = corner_radius.z;\n        center = vec2(size.x - radius, size.y - radius);\n    } else if (coords.x < corner_radius.w && size.y - corner_radius.w < coords.y) {\n        radius = corner_radius.w;\n        center = vec2(radius, size.y - radius);\n    } else {\n        return 1.0;\n    }\n\n    float dist = distance(coords, center);\n    float half_px = 0.5 / niri_scale;\n    return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);\n}\n\nvoid main() {\n    vec3 coords_geo = input_to_geo * vec3(v_coords, 1.0);\n\n    // Sample the texture.\n    vec4 color = texture2D(tex, v_coords);\n#if defined(NO_ALPHA)\n    color = vec4(color.rgb, 1.0);\n#endif\n\n    if (coords_geo.x < 0.0 || 1.0 < coords_geo.x || coords_geo.y < 0.0 || 1.0 < coords_geo.y) {\n        // Clip outside geometry.\n        color = vec4(0.0);\n    } else {\n        // Apply corner rounding inside geometry.\n        color = color * rounding_alpha(coords_geo.xy * geo_size, geo_size);\n    }\n\n    // Apply final alpha and tint.\n    color = color * alpha;\n\n#if defined(DEBUG_FLAGS)\n    if (tint == 1.0)\n        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;\n#endif\n\n    gl_FragColor = color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/close_epilogue.frag",
    "content": "\nvoid main() {\n    vec3 coords_geo = niri_input_to_geo * vec3(niri_v_coords, 1.0);\n    vec3 size_geo = vec3(niri_geo_size, 1.0);\n\n    vec4 color = close_color(coords_geo, size_geo);\n\n    color = color * niri_alpha;\n\n#if defined(DEBUG_FLAGS)\n    if (niri_tint == 1.0)\n        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;\n#endif\n\n    gl_FragColor = color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/close_prelude.frag",
    "content": "precision highp float;\n\n#if defined(DEBUG_FLAGS)\nuniform float niri_tint;\n#endif\n\nvarying vec2 niri_v_coords;\nuniform vec2 niri_size;\n\nuniform mat3 niri_input_to_geo;\nuniform vec2 niri_geo_size;\n\nuniform sampler2D niri_tex;\nuniform mat3 niri_geo_to_tex;\n\nuniform float niri_progress;\nuniform float niri_clamped_progress;\nuniform float niri_random_seed;\n\nuniform float niri_alpha;\nuniform float niri_scale;\n\n"
  },
  {
    "path": "src/render_helpers/shaders/gradient_fade.frag",
    "content": "#version 100\n\n//_DEFINES_\n\n#if defined(EXTERNAL)\n#extension GL_OES_EGL_image_external : require\n#endif\n\nprecision highp float;\n#if defined(EXTERNAL)\nuniform samplerExternalOES tex;\n#else\nuniform sampler2D tex;\n#endif\n\nuniform float alpha;\nvarying vec2 v_coords;\n\n#if defined(DEBUG_FLAGS)\nuniform float tint;\n#endif\n\n// x is left edge, y is right edge of the gradient.\nuniform vec2 cutoff;\n\nvoid main() {\n    // Sample the texture.\n    vec4 color = texture2D(tex, v_coords);\n#if defined(NO_ALPHA)\n    color = vec4(color.rgb, 1.0);\n#endif\n\n    if (cutoff.x < cutoff.y) {\n        float fade = clamp((cutoff.y - v_coords.x) / (cutoff.y - cutoff.x), 0.0, 1.0);\n        color = color * fade;\n    }\n\n    // Apply final alpha and tint.\n    color = color * alpha;\n\n#if defined(DEBUG_FLAGS)\n    if (tint == 1.0)\n        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;\n#endif\n\n    gl_FragColor = color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/mod.rs",
    "content": "use std::cell::RefCell;\n\nuse glam::Mat3;\nuse smithay::backend::renderer::gles::{\n    GlesError, GlesFrame, GlesRenderer, GlesTexProgram, Uniform, UniformName, UniformType,\n    UniformValue,\n};\n\nuse super::renderer::NiriRenderer;\nuse super::shader_element::ShaderProgram;\n\npub struct Shaders {\n    pub border: Option<ShaderProgram>,\n    pub shadow: Option<ShaderProgram>,\n    pub clipped_surface: Option<GlesTexProgram>,\n    pub resize: Option<ShaderProgram>,\n    pub gradient_fade: Option<GlesTexProgram>,\n    pub custom_resize: RefCell<Option<ShaderProgram>>,\n    pub custom_close: RefCell<Option<ShaderProgram>>,\n    pub custom_open: RefCell<Option<ShaderProgram>>,\n}\n\n#[derive(Debug, Clone, Copy)]\npub enum ProgramType {\n    Border,\n    Shadow,\n    Resize,\n    Close,\n    Open,\n}\n\nimpl Shaders {\n    fn compile(renderer: &mut GlesRenderer) -> Self {\n        let _span = tracy_client::span!(\"Shaders::compile\");\n\n        let border = ShaderProgram::compile(\n            renderer,\n            include_str!(\"border.frag\"),\n            &[\n                UniformName::new(\"colorspace\", UniformType::_1f),\n                UniformName::new(\"hue_interpolation\", UniformType::_1f),\n                UniformName::new(\"color_from\", UniformType::_4f),\n                UniformName::new(\"color_to\", UniformType::_4f),\n                UniformName::new(\"grad_offset\", UniformType::_2f),\n                UniformName::new(\"grad_width\", UniformType::_1f),\n                UniformName::new(\"grad_vec\", UniformType::_2f),\n                UniformName::new(\"input_to_geo\", UniformType::Matrix3x3),\n                UniformName::new(\"geo_size\", UniformType::_2f),\n                UniformName::new(\"outer_radius\", UniformType::_4f),\n                UniformName::new(\"border_width\", UniformType::_1f),\n            ],\n            &[],\n        )\n        .map_err(|err| {\n            warn!(\"error compiling border shader: {err:?}\");\n        })\n        .ok();\n\n        let shadow = ShaderProgram::compile(\n            renderer,\n            include_str!(\"shadow.frag\"),\n            &[\n                UniformName::new(\"shadow_color\", UniformType::_4f),\n                UniformName::new(\"sigma\", UniformType::_1f),\n                UniformName::new(\"input_to_geo\", UniformType::Matrix3x3),\n                UniformName::new(\"geo_size\", UniformType::_2f),\n                UniformName::new(\"corner_radius\", UniformType::_4f),\n                UniformName::new(\"window_input_to_geo\", UniformType::Matrix3x3),\n                UniformName::new(\"window_geo_size\", UniformType::_2f),\n                UniformName::new(\"window_corner_radius\", UniformType::_4f),\n            ],\n            &[],\n        )\n        .map_err(|err| {\n            warn!(\"error compiling shadow shader: {err:?}\");\n        })\n        .ok();\n\n        let clipped_surface = renderer\n            .compile_custom_texture_shader(\n                include_str!(\"clipped_surface.frag\"),\n                &[\n                    UniformName::new(\"niri_scale\", UniformType::_1f),\n                    UniformName::new(\"geo_size\", UniformType::_2f),\n                    UniformName::new(\"corner_radius\", UniformType::_4f),\n                    UniformName::new(\"input_to_geo\", UniformType::Matrix3x3),\n                ],\n            )\n            .map_err(|err| {\n                warn!(\"error compiling clipped surface shader: {err:?}\");\n            })\n            .ok();\n\n        let resize = compile_resize_program(renderer, include_str!(\"resize.frag\"))\n            .map_err(|err| {\n                warn!(\"error compiling resize shader: {err:?}\");\n            })\n            .ok();\n\n        let gradient_fade = renderer\n            .compile_custom_texture_shader(\n                include_str!(\"gradient_fade.frag\"),\n                &[UniformName::new(\"cutoff\", UniformType::_2f)],\n            )\n            .map_err(|err| {\n                warn!(\"error compiling gradient fade shader: {err:?}\");\n            })\n            .ok();\n\n        Self {\n            border,\n            shadow,\n            clipped_surface,\n            resize,\n            gradient_fade,\n            custom_resize: RefCell::new(None),\n            custom_close: RefCell::new(None),\n            custom_open: RefCell::new(None),\n        }\n    }\n\n    pub fn get_from_frame<'a>(frame: &'a mut GlesFrame<'_, '_>) -> &'a Self {\n        let data = frame.egl_context().user_data();\n        data.get()\n            .expect(\"shaders::init() must be called when creating the renderer\")\n    }\n\n    pub fn get(renderer: &mut impl NiriRenderer) -> &Self {\n        let renderer = renderer.as_gles_renderer();\n        let data = renderer.egl_context().user_data();\n        data.get()\n            .expect(\"shaders::init() must be called when creating the renderer\")\n    }\n\n    pub fn replace_custom_resize_program(\n        &self,\n        program: Option<ShaderProgram>,\n    ) -> Option<ShaderProgram> {\n        self.custom_resize.replace(program)\n    }\n\n    pub fn replace_custom_close_program(\n        &self,\n        program: Option<ShaderProgram>,\n    ) -> Option<ShaderProgram> {\n        self.custom_close.replace(program)\n    }\n\n    pub fn replace_custom_open_program(\n        &self,\n        program: Option<ShaderProgram>,\n    ) -> Option<ShaderProgram> {\n        self.custom_open.replace(program)\n    }\n\n    pub fn program(&self, program: ProgramType) -> Option<ShaderProgram> {\n        match program {\n            ProgramType::Border => self.border.clone(),\n            ProgramType::Shadow => self.shadow.clone(),\n            ProgramType::Resize => self\n                .custom_resize\n                .borrow()\n                .clone()\n                .or_else(|| self.resize.clone()),\n            ProgramType::Close => self.custom_close.borrow().clone(),\n            ProgramType::Open => self.custom_open.borrow().clone(),\n        }\n    }\n}\n\npub fn init(renderer: &mut GlesRenderer) {\n    let shaders = Shaders::compile(renderer);\n    let data = renderer.egl_context().user_data();\n    if !data.insert_if_missing(|| shaders) {\n        error!(\"shaders were already compiled\");\n    }\n}\n\nfn compile_resize_program(\n    renderer: &mut GlesRenderer,\n    src: &str,\n) -> Result<ShaderProgram, GlesError> {\n    let mut program = include_str!(\"resize_prelude.frag\").to_string();\n    program.push_str(src);\n    program.push_str(include_str!(\"resize_epilogue.frag\"));\n\n    ShaderProgram::compile(\n        renderer,\n        &program,\n        &[\n            UniformName::new(\"niri_input_to_curr_geo\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_curr_geo_to_prev_geo\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_curr_geo_to_next_geo\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_curr_geo_size\", UniformType::_2f),\n            UniformName::new(\"niri_geo_to_tex_prev\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_geo_to_tex_next\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_progress\", UniformType::_1f),\n            UniformName::new(\"niri_clamped_progress\", UniformType::_1f),\n            UniformName::new(\"niri_corner_radius\", UniformType::_4f),\n            UniformName::new(\"niri_clip_to_geometry\", UniformType::_1f),\n        ],\n        &[\"niri_tex_prev\", \"niri_tex_next\"],\n    )\n}\n\npub fn set_custom_resize_program(renderer: &mut GlesRenderer, src: Option<&str>) {\n    let program = if let Some(src) = src {\n        match compile_resize_program(renderer, src) {\n            Ok(program) => Some(program),\n            Err(err) => {\n                warn!(\"error compiling custom resize shader: {err:?}\");\n                return;\n            }\n        }\n    } else {\n        None\n    };\n\n    if let Some(prev) = Shaders::get(renderer).replace_custom_resize_program(program) {\n        if let Err(err) = prev.destroy(renderer) {\n            warn!(\"error destroying previous custom resize shader: {err:?}\");\n        }\n    }\n}\n\nfn compile_close_program(\n    renderer: &mut GlesRenderer,\n    src: &str,\n) -> Result<ShaderProgram, GlesError> {\n    let mut program = include_str!(\"close_prelude.frag\").to_string();\n    program.push_str(src);\n    program.push_str(include_str!(\"close_epilogue.frag\"));\n\n    ShaderProgram::compile(\n        renderer,\n        &program,\n        &[\n            UniformName::new(\"niri_input_to_geo\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_geo_size\", UniformType::_2f),\n            UniformName::new(\"niri_geo_to_tex\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_progress\", UniformType::_1f),\n            UniformName::new(\"niri_clamped_progress\", UniformType::_1f),\n            UniformName::new(\"niri_random_seed\", UniformType::_1f),\n        ],\n        &[\"niri_tex\"],\n    )\n}\n\npub fn set_custom_close_program(renderer: &mut GlesRenderer, src: Option<&str>) {\n    let program = if let Some(src) = src {\n        match compile_close_program(renderer, src) {\n            Ok(program) => Some(program),\n            Err(err) => {\n                warn!(\"error compiling custom close shader: {err:?}\");\n                return;\n            }\n        }\n    } else {\n        None\n    };\n\n    if let Some(prev) = Shaders::get(renderer).replace_custom_close_program(program) {\n        if let Err(err) = prev.destroy(renderer) {\n            warn!(\"error destroying previous custom close shader: {err:?}\");\n        }\n    }\n}\n\nfn compile_open_program(\n    renderer: &mut GlesRenderer,\n    src: &str,\n) -> Result<ShaderProgram, GlesError> {\n    let mut program = include_str!(\"open_prelude.frag\").to_string();\n    program.push_str(src);\n    program.push_str(include_str!(\"open_epilogue.frag\"));\n\n    ShaderProgram::compile(\n        renderer,\n        &program,\n        &[\n            UniformName::new(\"niri_input_to_geo\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_geo_size\", UniformType::_2f),\n            UniformName::new(\"niri_geo_to_tex\", UniformType::Matrix3x3),\n            UniformName::new(\"niri_progress\", UniformType::_1f),\n            UniformName::new(\"niri_clamped_progress\", UniformType::_1f),\n            UniformName::new(\"niri_random_seed\", UniformType::_1f),\n        ],\n        &[\"niri_tex\"],\n    )\n}\n\npub fn set_custom_open_program(renderer: &mut GlesRenderer, src: Option<&str>) {\n    let program = if let Some(src) = src {\n        match compile_open_program(renderer, src) {\n            Ok(program) => Some(program),\n            Err(err) => {\n                warn!(\"error compiling custom open shader: {err:?}\");\n                return;\n            }\n        }\n    } else {\n        None\n    };\n\n    if let Some(prev) = Shaders::get(renderer).replace_custom_open_program(program) {\n        if let Err(err) = prev.destroy(renderer) {\n            warn!(\"error destroying previous custom open shader: {err:?}\");\n        }\n    }\n}\n\npub fn mat3_uniform(name: &str, mat: Mat3) -> Uniform<'_> {\n    Uniform::new(\n        name,\n        UniformValue::Matrix3x3 {\n            matrices: vec![mat.to_cols_array()],\n            transpose: false,\n        },\n    )\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/open_epilogue.frag",
    "content": "\nvoid main() {\n    vec3 coords_geo = niri_input_to_geo * vec3(niri_v_coords, 1.0);\n    vec3 size_geo = vec3(niri_geo_size, 1.0);\n\n    vec4 color = open_color(coords_geo, size_geo);\n\n    color = color * niri_alpha;\n\n#if defined(DEBUG_FLAGS)\n    if (niri_tint == 1.0)\n        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;\n#endif\n\n    gl_FragColor = color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/open_prelude.frag",
    "content": "precision highp float;\n\n#if defined(DEBUG_FLAGS)\nuniform float niri_tint;\n#endif\n\nvarying vec2 niri_v_coords;\nuniform vec2 niri_size;\n\nuniform mat3 niri_input_to_geo;\nuniform vec2 niri_geo_size;\n\nuniform sampler2D niri_tex;\nuniform mat3 niri_geo_to_tex;\n\nuniform float niri_progress;\nuniform float niri_clamped_progress;\nuniform float niri_random_seed;\n\nuniform float niri_alpha;\nuniform float niri_scale;\n\n"
  },
  {
    "path": "src/render_helpers/shaders/resize.frag",
    "content": "vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) {\n    vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo;\n    vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st);\n\n    vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo;\n    vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st);\n\n    vec4 color = mix(color_prev, color_next, niri_clamped_progress);\n    return color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/resize_epilogue.frag",
    "content": "\nvoid main() {\n    vec3 coords_curr_geo = niri_input_to_curr_geo * vec3(niri_v_coords, 1.0);\n    vec3 size_curr_geo = vec3(niri_curr_geo_size, 1.0);\n\n    vec4 color = resize_color(coords_curr_geo, size_curr_geo);\n\n    if (niri_clip_to_geometry == 1.0) {\n        if (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x\n                || coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y) {\n            // Clip outside geometry.\n            color = vec4(0.0);\n        } else {\n            // Apply corner rounding inside geometry.\n            color = color * niri_rounding_alpha(coords_curr_geo.xy * size_curr_geo.xy, size_curr_geo.xy);\n        }\n    }\n\n    color = color * niri_alpha;\n\n#if defined(DEBUG_FLAGS)\n    if (niri_tint == 1.0)\n        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;\n#endif\n\n    gl_FragColor = color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/resize_prelude.frag",
    "content": "precision highp float;\n\n#if defined(DEBUG_FLAGS)\nuniform float niri_tint;\n#endif\n\nvarying vec2 niri_v_coords;\nuniform vec2 niri_size;\n\nuniform mat3 niri_input_to_curr_geo;\nuniform mat3 niri_curr_geo_to_prev_geo;\nuniform mat3 niri_curr_geo_to_next_geo;\nuniform vec2 niri_curr_geo_size;\n\nuniform sampler2D niri_tex_prev;\nuniform mat3 niri_geo_to_tex_prev;\n\nuniform sampler2D niri_tex_next;\nuniform mat3 niri_geo_to_tex_next;\n\nuniform float niri_progress;\nuniform float niri_clamped_progress;\n\nuniform vec4 niri_corner_radius;\nuniform float niri_clip_to_geometry;\n\nuniform float niri_alpha;\nuniform float niri_scale;\n\nfloat niri_rounding_alpha(vec2 coords, vec2 size) {\n    vec2 center;\n    float radius;\n\n    if (coords.x < niri_corner_radius.x && coords.y < niri_corner_radius.x) {\n        radius = niri_corner_radius.x;\n        center = vec2(radius, radius);\n    } else if (size.x - niri_corner_radius.y < coords.x && coords.y < niri_corner_radius.y) {\n        radius = niri_corner_radius.y;\n        center = vec2(size.x - radius, radius);\n    } else if (size.x - niri_corner_radius.z < coords.x && size.y - niri_corner_radius.z < coords.y) {\n        radius = niri_corner_radius.z;\n        center = vec2(size.x - radius, size.y - radius);\n    } else if (coords.x < niri_corner_radius.w && size.y - niri_corner_radius.w < coords.y) {\n        radius = niri_corner_radius.w;\n        center = vec2(radius, size.y - radius);\n    } else {\n        return 1.0;\n    }\n\n    float dist = distance(coords, center);\n    float half_px = 0.5 / niri_scale;\n    return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/shadow.frag",
    "content": "precision highp float;\n\n#if defined(DEBUG_FLAGS)\nuniform float niri_tint;\n#endif\n\nuniform float niri_alpha;\nuniform float niri_scale;\n\nuniform vec2 niri_size;\nvarying vec2 niri_v_coords;\n\nuniform vec4 shadow_color;\nuniform float sigma;\n\nuniform mat3 input_to_geo;\nuniform vec2 geo_size;\nuniform vec4 corner_radius;\n\nuniform mat3 window_input_to_geo;\nuniform vec2 window_geo_size;\nuniform vec4 window_corner_radius;\n\n// Based on: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/\n//\n// License: CC0 (http://creativecommons.org/publicdomain/zero/1.0/)\n\n// A standard gaussian function, used for weighting samples\nfloat gaussian(float x, float sigma) {\n  const float pi = 3.141592653589793;\n  return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma);\n}\n\n// This approximates the error function, needed for the gaussian integral\nvec2 erf(vec2 x) {\n  vec2 s = sign(x), a = abs(x);\n  x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;\n  x *= x;\n  return s - s / (x * x);\n}\n\n// Return the blurred mask along the x dimension\nfloat roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) {\n  float delta = min(halfSize.y - corner - abs(y), 0.0);\n  float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta));\n  vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma));\n  return integral.y - integral.x;\n}\n\n// Return the mask for the shadow of a box from lower to upper\nfloat roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner) {\n  // Center everything to make the math easier\n  vec2 center = (lower + upper) * 0.5;\n  vec2 halfSize = (upper - lower) * 0.5;\n  point -= center;\n\n  // The signal is only non-zero in a limited range, so don't waste samples\n  float low = point.y - halfSize.y;\n  float high = point.y + halfSize.y;\n  float start = clamp(-3.0 * sigma, low, high);\n  float end = clamp(3.0 * sigma, low, high);\n\n  // Accumulate samples (we can get away with surprisingly few samples)\n  float step = (end - start) / 4.0;\n  float y = start + step * 0.5;\n  float value = 0.0;\n  for (int i = 0; i < 4; i++) {\n    value += roundedBoxShadowX(point.x, point.y - y, sigma, corner, halfSize) * gaussian(y, sigma) * step;\n    y += step;\n  }\n\n  return value;\n}\n\nfloat rounding_alpha(vec2 coords, vec2 size, vec4 corner_radius) {\n    vec2 center;\n    float radius;\n\n    if (coords.x < corner_radius.x && coords.y < corner_radius.x) {\n        radius = corner_radius.x;\n        center = vec2(radius, radius);\n    } else if (size.x - corner_radius.y < coords.x && coords.y < corner_radius.y) {\n        radius = corner_radius.y;\n        center = vec2(size.x - radius, radius);\n    } else if (size.x - corner_radius.z < coords.x && size.y - corner_radius.z < coords.y) {\n        radius = corner_radius.z;\n        center = vec2(size.x - radius, size.y - radius);\n    } else if (coords.x < corner_radius.w && size.y - corner_radius.w < coords.y) {\n        radius = corner_radius.w;\n        center = vec2(radius, size.y - radius);\n    } else {\n        return 1.0;\n    }\n\n    float dist = distance(coords, center);\n    float half_px = 0.5 / niri_scale;\n    return 1.0 - smoothstep(radius - half_px, radius + half_px, dist);\n}\n\nvoid main() {\n    vec3 coords_geo = input_to_geo * vec3(niri_v_coords, 1.0);\n    vec3 coords_window_geo = window_input_to_geo * vec3(niri_v_coords, 1.0);\n\n    vec4 color = shadow_color;\n\n    float shadow_value;\n    if (sigma < 0.1) {\n        // With low enough sigma just draw a rounded rectangle.\n        shadow_value = rounding_alpha(coords_geo.xy, geo_size, corner_radius);\n    } else {\n        shadow_value = roundedBoxShadow(\n            vec2(0.0, 0.0),\n            geo_size,\n            coords_geo.xy,\n            sigma,\n            // FIXME: figure out how to blur with different corner radii.\n            //\n            // GTK seems to call blurring separately for the rect and for the 4 corners:\n            // https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-4-16/gsk/gpu/shaders/gskgpuboxshadow.glsl\n            corner_radius.x\n        );\n    }\n    color = color * shadow_value;\n\n    // Cut out the inside of the window geometry if requested.\n    if (window_geo_size != vec2(0.0, 0.0)) {\n        if (0.0 <= coords_window_geo.x && coords_window_geo.x <= window_geo_size.x\n                && 0.0 <= coords_window_geo.y && coords_window_geo.y <= window_geo_size.y) {\n            float alpha = rounding_alpha(coords_window_geo.xy, window_geo_size, window_corner_radius);\n            color = color * (1.0 - alpha);\n        }\n    }\n\n    color = color * niri_alpha;\n\n#if defined(DEBUG_FLAGS)\n    if (niri_tint == 1.0)\n        color = vec4(0.0, 0.2, 0.0, 0.2) + color * 0.8;\n#endif\n\n    gl_FragColor = color;\n}\n"
  },
  {
    "path": "src/render_helpers/shaders/texture.vert",
    "content": "#version 100\n\nuniform mat3 matrix;\nuniform mat3 tex_matrix;\n\nattribute vec2 vert;\nattribute vec4 vert_position;\n\nvarying vec2 niri_v_coords;\n\nmat2 scale(vec2 scale_vec){\n    return mat2(\n        scale_vec.x, 0.0,\n        0.0, scale_vec.y\n    );\n}\n\nvoid main() {\n    vec2 vert_transform_translation = vert_position.xy;\n    vec2 vert_transform_scale = vert_position.zw;\n    vec3 position = vec3(vert * scale(vert_transform_scale) + vert_transform_translation, 1.0);\n    niri_v_coords = (tex_matrix * position).xy;\n    gl_Position = vec4(matrix * position, 1.0);\n}\n\n"
  },
  {
    "path": "src/render_helpers/shadow.rs",
    "content": "use std::collections::HashMap;\nuse std::rc::Rc;\n\nuse glam::{Mat3, Vec2};\nuse niri_config::{Color, CornerRadius};\nuse smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::{GlesError, GlesFrame, GlesRenderer, Uniform};\nuse smithay::backend::renderer::utils::{CommitCounter, DamageSet, OpaqueRegions};\nuse smithay::gpu_span_location;\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};\n\nuse super::renderer::NiriRenderer;\nuse super::shader_element::ShaderRenderElement;\nuse super::shaders::{mat3_uniform, ProgramType, Shaders};\nuse crate::backend::tty::{TtyFrame, TtyRenderer, TtyRendererError};\nuse crate::render_helpers::renderer::AsGlesFrame as _;\n\n/// Renders a rounded rectangle shadow.\n#[derive(Debug, Clone)]\npub struct ShadowRenderElement {\n    inner: ShaderRenderElement,\n    params: Parameters,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\nstruct Parameters {\n    size: Size<f64, Logical>,\n    geometry: Rectangle<f64, Logical>,\n    color: Color,\n    sigma: f32,\n    corner_radius: CornerRadius,\n    // Should only be used for visual improvements, i.e. corner radius anti-aliasing.\n    scale: f32,\n    alpha: f32,\n\n    window_geometry: Rectangle<f64, Logical>,\n    window_corner_radius: CornerRadius,\n}\n\nimpl ShadowRenderElement {\n    #[allow(clippy::too_many_arguments)]\n    pub fn new(\n        size: Size<f64, Logical>,\n        geometry: Rectangle<f64, Logical>,\n        color: Color,\n        sigma: f32,\n        corner_radius: CornerRadius,\n        scale: f32,\n        window_geometry: Rectangle<f64, Logical>,\n        window_corner_radius: CornerRadius,\n        alpha: f32,\n    ) -> Self {\n        let inner = ShaderRenderElement::empty(ProgramType::Shadow, Kind::Unspecified);\n        let mut rv = Self {\n            inner,\n            params: Parameters {\n                size,\n                geometry,\n                color,\n                sigma,\n                corner_radius,\n                scale,\n                alpha,\n                window_geometry,\n                window_corner_radius,\n            },\n        };\n        rv.update_inner();\n        rv\n    }\n\n    pub fn empty() -> Self {\n        let inner = ShaderRenderElement::empty(ProgramType::Shadow, Kind::Unspecified);\n        Self {\n            inner,\n            params: Parameters {\n                size: Default::default(),\n                geometry: Default::default(),\n                color: Default::default(),\n                sigma: 0.,\n                corner_radius: Default::default(),\n                scale: 1.,\n                alpha: 1.,\n                window_geometry: Default::default(),\n                window_corner_radius: Default::default(),\n            },\n        }\n    }\n\n    pub fn damage_all(&mut self) {\n        self.inner.damage_all();\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn update(\n        &mut self,\n        size: Size<f64, Logical>,\n        geometry: Rectangle<f64, Logical>,\n        color: Color,\n        sigma: f32,\n        corner_radius: CornerRadius,\n        scale: f32,\n        window_geometry: Rectangle<f64, Logical>,\n        window_corner_radius: CornerRadius,\n        alpha: f32,\n    ) {\n        let params = Parameters {\n            size,\n            geometry,\n            color,\n            sigma,\n            alpha,\n            corner_radius,\n            scale,\n            window_geometry,\n            window_corner_radius,\n        };\n        if self.params == params {\n            return;\n        }\n\n        self.params = params;\n        self.update_inner();\n    }\n\n    fn update_inner(&mut self) {\n        let Parameters {\n            size,\n            geometry,\n            color,\n            sigma,\n            alpha,\n            corner_radius,\n            scale,\n            window_geometry,\n            window_corner_radius,\n        } = self.params;\n\n        let area_size = Vec2::new(size.w as f32, size.h as f32);\n\n        let geo_loc = Vec2::new(geometry.loc.x as f32, geometry.loc.y as f32);\n        let geo_size = Vec2::new(geometry.size.w as f32, geometry.size.h as f32);\n\n        let input_to_geo =\n            Mat3::from_scale(area_size) * Mat3::from_translation(-geo_loc / area_size);\n\n        let window_geo_loc = Vec2::new(window_geometry.loc.x as f32, window_geometry.loc.y as f32);\n        let window_geo_size =\n            Vec2::new(window_geometry.size.w as f32, window_geometry.size.h as f32);\n\n        let window_input_to_geo =\n            Mat3::from_scale(area_size) * Mat3::from_translation(-window_geo_loc / area_size);\n\n        self.inner.update(\n            size,\n            None,\n            scale,\n            alpha,\n            Rc::new([\n                Uniform::new(\"shadow_color\", color.to_array_premul()),\n                Uniform::new(\"sigma\", sigma),\n                mat3_uniform(\"input_to_geo\", input_to_geo),\n                Uniform::new(\"geo_size\", geo_size.to_array()),\n                Uniform::new(\"corner_radius\", <[f32; 4]>::from(corner_radius)),\n                mat3_uniform(\"window_input_to_geo\", window_input_to_geo),\n                Uniform::new(\"window_geo_size\", window_geo_size.to_array()),\n                Uniform::new(\n                    \"window_corner_radius\",\n                    <[f32; 4]>::from(window_corner_radius),\n                ),\n            ]),\n            HashMap::new(),\n        );\n    }\n\n    pub fn with_location(mut self, location: Point<f64, Logical>) -> Self {\n        self.inner = self.inner.with_location(location);\n        self\n    }\n\n    pub fn with_alpha(mut self, alpha: f32) -> Self {\n        self.inner = self.inner.with_alpha(alpha);\n        self\n    }\n\n    pub fn has_shader(renderer: &mut impl NiriRenderer) -> bool {\n        Shaders::get(renderer)\n            .program(ProgramType::Shadow)\n            .is_some()\n    }\n}\n\nimpl Default for ShadowRenderElement {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\nimpl Element for ShadowRenderElement {\n    fn id(&self) -> &Id {\n        self.inner.id()\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.inner.current_commit()\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.inner.geometry(scale)\n    }\n\n    fn transform(&self) -> Transform {\n        self.inner.transform()\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        self.inner.src()\n    }\n\n    fn damage_since(\n        &self,\n        scale: Scale<f64>,\n        commit: Option<CommitCounter>,\n    ) -> DamageSet<i32, Physical> {\n        self.inner.damage_since(scale, commit)\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        self.inner.opaque_regions(scale)\n    }\n\n    fn alpha(&self) -> f32 {\n        self.inner.alpha()\n    }\n\n    fn kind(&self) -> Kind {\n        self.inner.kind()\n    }\n}\n\nimpl RenderElement<GlesRenderer> for ShadowRenderElement {\n    fn draw(\n        &self,\n        frame: &mut GlesFrame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), GlesError> {\n        let _span = tracy_client::span!(\"ShadowRenderElement::draw\");\n        frame.with_gpu_span(gpu_span_location!(\"ShadowRenderElement::draw\"), |frame| {\n            RenderElement::<GlesRenderer>::draw(\n                &self.inner,\n                frame,\n                src,\n                dst,\n                damage,\n                opaque_regions,\n            )\n        })\n    }\n\n    fn underlying_storage(&self, renderer: &mut GlesRenderer) -> Option<UnderlyingStorage<'_>> {\n        self.inner.underlying_storage(renderer)\n    }\n}\n\nimpl<'render> RenderElement<TtyRenderer<'render>> for ShadowRenderElement {\n    fn draw(\n        &self,\n        frame: &mut TtyFrame<'_, '_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), TtyRendererError<'render>> {\n        let frame = frame.as_gles_frame();\n        RenderElement::<GlesRenderer>::draw(self, frame, src, dst, damage, opaque_regions)?;\n        Ok(())\n    }\n\n    fn underlying_storage(\n        &self,\n        renderer: &mut TtyRenderer<'render>,\n    ) -> Option<UnderlyingStorage<'_>> {\n        self.inner.underlying_storage(renderer)\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/snapshot.rs",
    "content": "use std::cell::OnceCell;\n\nuse niri_config::BlockOutFrom;\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::renderer::element::{Kind, RenderElement};\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\nuse smithay::utils::{Logical, Physical, Point, Rectangle, Scale, Size, Transform};\n\nuse super::{render_to_encompassing_texture, RenderTarget, ToRenderElement};\n\n/// Snapshot of a render.\n#[derive(Debug)]\npub struct RenderSnapshot<C, B> {\n    /// Contents for a normal render.\n    ///\n    /// Relative to the geometry.\n    pub contents: Vec<C>,\n\n    /// Blocked-out contents.\n    ///\n    /// Relative to the geometry.\n    pub blocked_out_contents: Vec<B>,\n\n    /// Where the contents were blocked out from at the time of the snapshot.\n    pub block_out_from: Option<BlockOutFrom>,\n\n    /// Visual size of the element at the point of the snapshot.\n    pub size: Size<f64, Logical>,\n\n    /// Contents rendered into a texture (lazily).\n    pub texture: OnceCell<Option<(GlesTexture, Rectangle<i32, Physical>)>>,\n\n    /// Blocked-out contents rendered into a texture (lazily).\n    pub blocked_out_texture: OnceCell<Option<(GlesTexture, Rectangle<i32, Physical>)>>,\n}\n\nimpl<C, B, EC, EB> RenderSnapshot<C, B>\nwhere\n    C: ToRenderElement<RenderElement = EC>,\n    B: ToRenderElement<RenderElement = EB>,\n    EC: RenderElement<GlesRenderer>,\n    EB: RenderElement<GlesRenderer>,\n{\n    pub fn texture(\n        &self,\n        renderer: &mut GlesRenderer,\n        scale: Scale<f64>,\n        target: RenderTarget,\n    ) -> Option<&(GlesTexture, Rectangle<i32, Physical>)> {\n        if target.should_block_out(self.block_out_from) {\n            self.blocked_out_texture.get_or_init(|| {\n                let _span = tracy_client::span!(\"RenderSnapshot::texture\");\n\n                let elements: Vec<_> = self\n                    .blocked_out_contents\n                    .iter()\n                    .map(|baked| {\n                        baked.to_render_element(Point::from((0., 0.)), scale, 1., Kind::Unspecified)\n                    })\n                    .collect();\n\n                match render_to_encompassing_texture(\n                    renderer,\n                    scale,\n                    Transform::Normal,\n                    Fourcc::Abgr8888,\n                    &elements,\n                ) {\n                    Ok((texture, _sync_point, geo)) => Some((texture, geo)),\n                    Err(err) => {\n                        warn!(\"error rendering blocked-out contents to texture: {err:?}\");\n                        None\n                    }\n                }\n            })\n        } else {\n            self.texture.get_or_init(|| {\n                let _span = tracy_client::span!(\"RenderSnapshot::texture\");\n\n                let elements: Vec<_> = self\n                    .contents\n                    .iter()\n                    .map(|baked| {\n                        baked.to_render_element(Point::from((0., 0.)), scale, 1., Kind::Unspecified)\n                    })\n                    .collect();\n\n                match render_to_encompassing_texture(\n                    renderer,\n                    scale,\n                    Transform::Normal,\n                    Fourcc::Abgr8888,\n                    &elements,\n                ) {\n                    Ok((texture, _sync_point, geo)) => Some((texture, geo)),\n                    Err(err) => {\n                        warn!(\"error rendering contents to texture: {err:?}\");\n                        None\n                    }\n                }\n            })\n        }\n        .as_ref()\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/solid_color.rs",
    "content": "use smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};\nuse smithay::backend::renderer::{Color32F, Frame as _, Renderer};\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size};\n\n/// Smithay's solid color buffer, but with fractional scale.\n#[derive(Debug, Clone)]\npub struct SolidColorBuffer {\n    id: Id,\n    size: Size<f64, Logical>,\n    commit: CommitCounter,\n    color: Color32F,\n}\n\n/// Render element for a [`SolidColorBuffer`].\n#[derive(Debug, Clone)]\npub struct SolidColorRenderElement {\n    id: Id,\n    geometry: Rectangle<f64, Logical>,\n    commit: CommitCounter,\n    color: Color32F,\n    kind: Kind,\n}\n\nimpl Default for SolidColorBuffer {\n    fn default() -> Self {\n        Self {\n            id: Id::new(),\n            size: Default::default(),\n            commit: Default::default(),\n            color: Default::default(),\n        }\n    }\n}\n\nimpl SolidColorBuffer {\n    pub fn new(size: impl Into<Size<f64, Logical>>, color: impl Into<Color32F>) -> Self {\n        SolidColorBuffer {\n            id: Id::new(),\n            color: color.into(),\n            commit: CommitCounter::default(),\n            size: size.into(),\n        }\n    }\n\n    pub fn resize(&mut self, size: impl Into<Size<f64, Logical>>) {\n        let size = size.into();\n        if size != self.size {\n            self.size = size;\n            self.commit.increment();\n        }\n    }\n\n    pub fn set_color(&mut self, color: impl Into<Color32F>) {\n        let color = color.into();\n        if color != self.color {\n            self.color = color;\n            self.commit.increment();\n        }\n    }\n\n    pub fn update(&mut self, size: impl Into<Size<f64, Logical>>, color: impl Into<Color32F>) {\n        let size = size.into();\n        let color = color.into();\n        if size != self.size || color != self.color {\n            self.size = size;\n            self.color = color;\n            self.commit.increment();\n        }\n    }\n\n    pub fn color(&self) -> Color32F {\n        self.color\n    }\n\n    pub fn size(&self) -> Size<f64, Logical> {\n        self.size\n    }\n}\n\nimpl SolidColorRenderElement {\n    pub fn from_buffer(\n        buffer: &SolidColorBuffer,\n        location: impl Into<Point<f64, Logical>>,\n        alpha: f32,\n        kind: Kind,\n    ) -> Self {\n        let geo = Rectangle::new(location.into(), buffer.size());\n        let color = buffer.color * alpha;\n        Self::new(buffer.id.clone(), geo, buffer.commit, color, kind)\n    }\n\n    pub fn new(\n        id: Id,\n        geometry: Rectangle<f64, Logical>,\n        commit: CommitCounter,\n        color: Color32F,\n        kind: Kind,\n    ) -> Self {\n        SolidColorRenderElement {\n            id,\n            geometry,\n            commit,\n            color,\n            kind,\n        }\n    }\n\n    pub fn color(&self) -> Color32F {\n        self.color\n    }\n\n    pub fn geo(&self) -> Rectangle<f64, Logical> {\n        self.geometry\n    }\n}\n\nimpl Element for SolidColorRenderElement {\n    fn id(&self) -> &Id {\n        &self.id\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.commit\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        Rectangle::from_size(Size::from((1., 1.)))\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        self.geometry.to_physical_precise_round(scale)\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        if self.color.is_opaque() {\n            let rect = Rectangle::from_size(self.geometry.size).to_physical_precise_down(scale);\n            OpaqueRegions::from_slice(&[rect])\n        } else {\n            OpaqueRegions::default()\n        }\n    }\n\n    fn alpha(&self) -> f32 {\n        self.color.a()\n    }\n\n    fn kind(&self) -> Kind {\n        self.kind\n    }\n}\n\nimpl<R: Renderer> RenderElement<R> for SolidColorRenderElement {\n    fn draw(\n        &self,\n        frame: &mut R::Frame<'_, '_>,\n        _src: Rectangle<f64, Buffer>,\n        dst: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        _opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), R::Error> {\n        frame.draw_solid(dst, damage, self.color)\n    }\n\n    #[inline]\n    fn underlying_storage(&self, _renderer: &mut R) -> Option<UnderlyingStorage<'_>> {\n        None\n    }\n}\n"
  },
  {
    "path": "src/render_helpers/surface.rs",
    "content": "use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\nuse smithay::backend::renderer::utils::{import_surface, RendererSurfaceStateUserData};\nuse smithay::backend::renderer::{ImportAll, Renderer};\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::utils::{Logical, Physical, Point, Scale};\nuse smithay::wayland::compositor::{with_surface_tree_downward, TraversalAction};\n\nuse super::texture::TextureBuffer;\nuse super::BakedBuffer;\n\n/// Renders elements from a surface tree as textures into `storage`.\npub fn render_snapshot_from_surface_tree(\n    renderer: &mut GlesRenderer,\n    surface: &WlSurface,\n    location: Point<f64, Logical>,\n    storage: &mut Vec<BakedBuffer<TextureBuffer<GlesTexture>>>,\n) {\n    let _span = tracy_client::span!(\"render_snapshot_from_surface_tree\");\n\n    with_surface_tree_downward(\n        surface,\n        location,\n        |_, states, location| {\n            let mut location = *location;\n            let data = states.data_map.get::<RendererSurfaceStateUserData>();\n\n            if let Some(data) = data {\n                let data = &*data.lock().unwrap();\n\n                if let Some(view) = data.view() {\n                    location += view.offset.to_f64();\n                    TraversalAction::DoChildren(location)\n                } else {\n                    TraversalAction::SkipChildren\n                }\n            } else {\n                TraversalAction::SkipChildren\n            }\n        },\n        |_, states, location| {\n            let mut location = *location;\n            let data = states.data_map.get::<RendererSurfaceStateUserData>();\n\n            if let Some(data) = data {\n                let Some(view) = data.lock().unwrap().view() else {\n                    return;\n                };\n                location += view.offset.to_f64();\n\n                if let Err(err) = import_surface(renderer, states) {\n                    warn!(\"failed to import surface: {err:?}\");\n                    return;\n                }\n\n                let data = data.lock().unwrap();\n                let Some(texture) = data.texture(renderer.context_id()) else {\n                    return;\n                };\n\n                let buffer = TextureBuffer::from_texture(\n                    renderer,\n                    texture.clone(),\n                    f64::from(data.buffer_scale()),\n                    data.buffer_transform(),\n                    Vec::new(),\n                );\n\n                let baked = BakedBuffer {\n                    buffer,\n                    location,\n                    src: Some(view.src),\n                    dst: Some(view.dst),\n                };\n\n                storage.push(baked);\n            }\n        },\n        |_, _, _| true,\n    );\n}\n\npub fn push_elements_from_surface_tree<R>(\n    renderer: &mut R,\n    surface: &WlSurface,\n    // Fractional scale expects surface buffers to be aligned to physical pixels.\n    location: Point<i32, Physical>,\n    scale: Scale<f64>,\n    alpha: f32,\n    kind: Kind,\n    push: &mut dyn FnMut(WaylandSurfaceRenderElement<R>),\n) where\n    R: Renderer + ImportAll,\n    R::TextureId: Clone + 'static,\n{\n    let _span = tracy_client::span!(\"push_elements_from_surface_tree\");\n\n    let location = location.to_f64();\n\n    with_surface_tree_downward(\n        surface,\n        location,\n        |_, states, location| {\n            let mut location = *location;\n            let data = states.data_map.get::<RendererSurfaceStateUserData>();\n\n            if let Some(data) = data {\n                if let Some(view) = data.lock().unwrap().view() {\n                    location += view.offset.to_f64().to_physical(scale);\n                    TraversalAction::DoChildren(location)\n                } else {\n                    TraversalAction::SkipChildren\n                }\n            } else {\n                TraversalAction::SkipChildren\n            }\n        },\n        |surface, states, location| {\n            let mut location = *location;\n            let data = states.data_map.get::<RendererSurfaceStateUserData>();\n\n            if let Some(data) = data {\n                let has_view = if let Some(view) = data.lock().unwrap().view() {\n                    location += view.offset.to_f64().to_physical(scale);\n                    true\n                } else {\n                    false\n                };\n\n                if has_view {\n                    match WaylandSurfaceRenderElement::from_surface(\n                        renderer, surface, states, location, alpha, kind,\n                    ) {\n                        Ok(Some(surface)) => push(surface),\n                        Ok(None) => {} // surface is not mapped\n                        Err(err) => {\n                            warn!(\"failed to import surface: {}\", err);\n                        }\n                    };\n                }\n            }\n        },\n        |_, _, _| true,\n    );\n}\n"
  },
  {
    "path": "src/render_helpers/texture.rs",
    "content": "use smithay::backend::allocator::Fourcc;\nuse smithay::backend::renderer::element::{Element, Id, Kind, RenderElement, UnderlyingStorage};\nuse smithay::backend::renderer::gles::GlesTexture;\nuse smithay::backend::renderer::utils::{CommitCounter, OpaqueRegions};\nuse smithay::backend::renderer::{ContextId, Frame as _, ImportMem, Renderer, Texture};\nuse smithay::utils::{Buffer, Logical, Physical, Point, Rectangle, Scale, Size, Transform};\n\nuse super::memory::MemoryBuffer;\n\n/// Smithay's texture buffer, but with fractional scale.\n#[derive(Debug, Clone)]\npub struct TextureBuffer<T: Texture> {\n    id: Id,\n    commit_counter: CommitCounter,\n    renderer_context_id: ContextId<T>,\n    texture: T,\n    scale: Scale<f64>,\n    transform: Transform,\n    opaque_regions: Vec<Rectangle<i32, Buffer>>,\n}\n\n/// Render element for a [`TextureBuffer`].\n#[derive(Debug, Clone)]\npub struct TextureRenderElement<T: Texture> {\n    buffer: TextureBuffer<T>,\n    location: Point<f64, Logical>,\n    alpha: f32,\n    src: Option<Rectangle<f64, Logical>>,\n    size: Option<Size<f64, Logical>>,\n    kind: Kind,\n}\n\nimpl<T: Texture> TextureBuffer<T> {\n    pub fn from_texture<R: Renderer<TextureId = T>>(\n        renderer: &R,\n        texture: T,\n        scale: impl Into<Scale<f64>>,\n        transform: Transform,\n        opaque_regions: Vec<Rectangle<i32, Buffer>>,\n    ) -> Self {\n        TextureBuffer {\n            id: Id::new(),\n            commit_counter: CommitCounter::default(),\n            renderer_context_id: renderer.context_id(),\n            texture,\n            scale: scale.into(),\n            transform,\n            opaque_regions,\n        }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn from_memory<R: Renderer<TextureId = T> + ImportMem>(\n        renderer: &mut R,\n        data: &[u8],\n        format: Fourcc,\n        size: impl Into<Size<i32, Buffer>>,\n        flipped: bool,\n        scale: impl Into<Scale<f64>>,\n        transform: Transform,\n        opaque_regions: Vec<Rectangle<i32, Buffer>>,\n    ) -> Result<Self, R::Error> {\n        let texture = renderer.import_memory(data, format, size.into(), flipped)?;\n        Ok(TextureBuffer::from_texture(\n            renderer,\n            texture,\n            scale,\n            transform,\n            opaque_regions,\n        ))\n    }\n\n    pub fn from_memory_buffer<R: Renderer<TextureId = T> + ImportMem>(\n        renderer: &mut R,\n        buffer: &MemoryBuffer,\n    ) -> Result<Self, R::Error> {\n        Self::from_memory(\n            renderer,\n            buffer.data(),\n            buffer.format(),\n            buffer.size(),\n            false,\n            buffer.scale(),\n            buffer.transform(),\n            Vec::new(),\n        )\n    }\n\n    pub fn texture(&self) -> &T {\n        &self.texture\n    }\n\n    pub fn texture_scale(&self) -> Scale<f64> {\n        self.scale\n    }\n\n    pub fn set_texture_scale(&mut self, scale: impl Into<Scale<f64>>) {\n        self.scale = scale.into();\n    }\n\n    pub fn texture_transform(&self) -> Transform {\n        self.transform\n    }\n\n    pub fn set_texture_transform(&mut self, transform: Transform) {\n        self.transform = transform;\n    }\n}\n\nimpl<T: Texture> TextureBuffer<T> {\n    pub fn logical_size(&self) -> Size<f64, Logical> {\n        self.texture\n            .size()\n            .to_f64()\n            .to_logical(self.scale, self.transform)\n    }\n}\n\nimpl TextureBuffer<GlesTexture> {\n    pub fn is_texture_reference_unique(&mut self) -> bool {\n        self.texture.is_unique_reference()\n    }\n}\n\nimpl<T: Texture> TextureRenderElement<T> {\n    pub fn from_texture_buffer(\n        buffer: TextureBuffer<T>,\n        location: impl Into<Point<f64, Logical>>,\n        alpha: f32,\n        src: Option<Rectangle<f64, Logical>>,\n        size: Option<Size<f64, Logical>>,\n        kind: Kind,\n    ) -> Self {\n        TextureRenderElement {\n            buffer,\n            location: location.into(),\n            alpha,\n            src,\n            size,\n            kind,\n        }\n    }\n\n    pub fn buffer(&self) -> &TextureBuffer<T> {\n        &self.buffer\n    }\n}\n\nimpl<T: Texture> TextureRenderElement<T> {\n    pub fn logical_size(&self) -> Size<f64, Logical> {\n        self.size\n            .or_else(|| self.src.map(|src| src.size))\n            .unwrap_or_else(|| self.buffer.logical_size())\n    }\n\n    pub fn logical_src(&self) -> Rectangle<f64, Logical> {\n        self.src\n            .unwrap_or_else(|| Rectangle::from_size(self.logical_size()))\n    }\n}\n\nimpl<T: Texture> Element for TextureRenderElement<T> {\n    fn id(&self) -> &Id {\n        &self.buffer.id\n    }\n\n    fn current_commit(&self) -> CommitCounter {\n        self.buffer.commit_counter\n    }\n\n    fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {\n        let logical_geo = Rectangle::new(self.location, self.logical_size());\n        logical_geo.to_physical_precise_round(scale)\n    }\n\n    fn transform(&self) -> Transform {\n        self.buffer.transform\n    }\n\n    fn src(&self) -> Rectangle<f64, Buffer> {\n        self.src\n            .map(|src| {\n                src.to_buffer(\n                    self.buffer.scale,\n                    self.buffer.transform,\n                    &self.buffer.logical_size(),\n                )\n            })\n            .unwrap_or_else(|| Rectangle::from_size(self.buffer.texture.size()).to_f64())\n    }\n\n    fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {\n        let texture_size = self.buffer.texture.size().to_f64();\n        let src = self.src();\n\n        self.buffer\n            .opaque_regions\n            .iter()\n            .filter_map(|region| {\n                let mut region = region.to_f64().intersection(src)?;\n\n                region.loc -= src.loc;\n                region = region.upscale(texture_size / src.size);\n\n                let logical =\n                    region.to_logical(self.buffer.scale, self.buffer.transform, &src.size);\n                Some(logical.to_physical_precise_down(scale))\n            })\n            .collect()\n    }\n\n    fn alpha(&self) -> f32 {\n        self.alpha\n    }\n\n    fn kind(&self) -> Kind {\n        self.kind\n    }\n}\n\nimpl<R, T> RenderElement<R> for TextureRenderElement<T>\nwhere\n    R: Renderer<TextureId = T>,\n    T: Texture,\n{\n    fn draw(\n        &self,\n        frame: &mut R::Frame<'_, '_>,\n        src: Rectangle<f64, Buffer>,\n        dest: Rectangle<i32, Physical>,\n        damage: &[Rectangle<i32, Physical>],\n        opaque_regions: &[Rectangle<i32, Physical>],\n    ) -> Result<(), R::Error> {\n        if frame.context_id() != self.buffer.renderer_context_id {\n            warn!(\"trying to render texture from different renderer\");\n            return Ok(());\n        }\n\n        frame.render_texture_from_to(\n            &self.buffer.texture,\n            src,\n            dest,\n            damage,\n            opaque_regions,\n            self.buffer.transform,\n            self.alpha,\n        )\n    }\n\n    fn underlying_storage(&self, _renderer: &mut R) -> Option<UnderlyingStorage<'_>> {\n        None\n    }\n}\n"
  },
  {
    "path": "src/rubber_band.rs",
    "content": "#[derive(Debug, Clone, Copy)]\npub struct RubberBand {\n    pub stiffness: f64,\n    pub limit: f64,\n}\n\nimpl RubberBand {\n    pub fn band(&self, x: f64) -> f64 {\n        let c = self.stiffness;\n        let d = self.limit;\n\n        (1. - (1. / (x * c / d + 1.))) * d\n    }\n\n    pub fn derivative(&self, x: f64) -> f64 {\n        let c = self.stiffness;\n        let d = self.limit;\n\n        c * d * d / (c * x + d).powi(2)\n    }\n\n    pub fn clamp(&self, min: f64, max: f64, x: f64) -> f64 {\n        let clamped = x.clamp(min, max);\n        let sign = if x < clamped { -1. } else { 1. };\n        let diff = (x - clamped).abs();\n\n        clamped + sign * self.band(diff)\n    }\n\n    pub fn clamp_derivative(&self, min: f64, max: f64, x: f64) -> f64 {\n        if min <= x && x <= max {\n            return 1.;\n        }\n\n        let clamped = x.clamp(min, max);\n        let diff = (x - clamped).abs();\n        self.derivative(diff)\n    }\n}\n"
  },
  {
    "path": "src/screencasting/mod.rs",
    "content": "use std::collections::hash_map::Entry;\nuse std::collections::{HashMap, HashSet};\nuse std::mem;\nuse std::time::Duration;\n\nuse anyhow::Context as _;\nuse calloop::LoopHandle;\nuse smithay::backend::allocator::format::FormatSet;\nuse smithay::backend::allocator::gbm::GbmDevice;\nuse smithay::backend::drm::DrmDeviceFd;\nuse smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::desktop::Window;\nuse smithay::output::Output;\nuse smithay::reexports::gbm::Modifier;\nuse smithay::utils::{Physical, Point, Scale, Size};\nuse zbus::object_server::SignalEmitter;\n\nuse crate::dbus::mutter_screen_cast::{self, CursorMode, ScreenCastToNiri, StreamTargetId};\nuse crate::niri::{CastTarget, Niri, OutputRenderElements, PointerRenderElements, State};\nuse crate::niri_render_elements;\nuse crate::render_helpers::RenderTarget;\nuse crate::utils::{get_monotonic_time, CastSessionId, CastStreamId};\nuse crate::window::mapped::{MappedId, WindowCastRenderElements};\n\nmod pw_utils;\nuse pw_utils::{Cast, CastSizeChange, CursorData, PipeWire, PwToNiri};\n\npub struct Screencasting {\n    pub casts: Vec<Cast>,\n\n    /// Dynamic-target casts waiting for their first target to start.\n    pub pending_dynamic_casts: Vec<PendingCast>,\n\n    pub pw_to_niri: calloop::channel::Sender<PwToNiri>,\n\n    /// Screencast output for each mapped window.\n    pub mapped_cast_output: HashMap<Window, Output>,\n\n    /// Window ID for the \"dynamic cast\" special window for the xdp-gnome picker.\n    pub dynamic_cast_id_for_portal: MappedId,\n\n    // Drop PipeWire last, and specifically after casts, to prevent a double-free (yay).\n    pub pipewire: Option<PipeWire>,\n}\n\n/// A screencast request that hasn't been started yet.\npub struct PendingCast {\n    pub session_id: CastSessionId,\n    pub stream_id: CastStreamId,\n    pub cursor_mode: CursorMode,\n    pub signal_ctx: SignalEmitter<'static>,\n}\n\nimpl Screencasting {\n    pub fn new(event_loop: &LoopHandle<'static, State>) -> Self {\n        let pw_to_niri = {\n            let (pw_to_niri, from_pipewire) = calloop::channel::channel();\n            event_loop\n                .insert_source(from_pipewire, move |event, _, state| match event {\n                    calloop::channel::Event::Msg(msg) => state.on_pw_msg(msg),\n                    calloop::channel::Event::Closed => (),\n                })\n                .unwrap();\n            pw_to_niri\n        };\n\n        Self {\n            casts: vec![],\n            pending_dynamic_casts: vec![],\n            pw_to_niri,\n            mapped_cast_output: HashMap::new(),\n            dynamic_cast_id_for_portal: MappedId::next(),\n            pipewire: None,\n        }\n    }\n}\n\nimpl State {\n    fn prepare_pw_cast(&mut self) -> anyhow::Result<(GbmDevice<DrmDeviceFd>, FormatSet)> {\n        let gbm = self\n            .backend\n            .gbm_device()\n            .context(\"no GBM device available\")?;\n\n        // Ensure PipeWire is initialized.\n        if self.niri.casting.pipewire.is_none() {\n            let pw = PipeWire::new(\n                self.niri.event_loop.clone(),\n                self.niri.casting.pw_to_niri.clone(),\n            )\n            .context(\"error initializing PipeWire\")?;\n            self.niri.casting.pipewire = Some(pw);\n        }\n\n        let mut render_formats = self\n            .backend\n            .with_primary_renderer(|renderer| {\n                renderer.egl_context().dmabuf_render_formats().clone()\n            })\n            .unwrap_or_default();\n\n        {\n            let config = self.niri.config.borrow();\n            if config.debug.force_pipewire_invalid_modifier {\n                render_formats = render_formats\n                    .into_iter()\n                    .filter(|f| f.modifier == Modifier::Invalid)\n                    .collect();\n            }\n        }\n\n        Ok((gbm, render_formats))\n    }\n\n    pub fn on_pw_msg(&mut self, msg: PwToNiri) {\n        match msg {\n            PwToNiri::StopCast { session_id } => self.niri.stop_cast(session_id),\n            PwToNiri::Redraw { stream_id } => self.redraw_cast(stream_id),\n            PwToNiri::FatalError => {\n                warn!(\"stopping PipeWire due to fatal error\");\n                let casting = &mut self.niri.casting;\n                if let Some(pw) = casting.pipewire.take() {\n                    let mut ids = HashSet::new();\n                    for cast in &casting.pending_dynamic_casts {\n                        ids.insert(cast.session_id);\n                    }\n                    for cast in &casting.casts {\n                        ids.insert(cast.session_id);\n                    }\n                    for id in ids {\n                        self.niri.stop_cast(id);\n                    }\n                    self.niri.event_loop.remove(pw.token);\n                }\n            }\n        }\n    }\n\n    fn redraw_cast(&mut self, stream_id: CastStreamId) {\n        let _span = tracy_client::span!(\"State::redraw_cast\");\n\n        let casts = &mut self.niri.casting.casts;\n        let Some(idx) = casts.iter().position(|cast| cast.stream_id == stream_id) else {\n            warn!(\"cast to redraw is missing\");\n            return;\n        };\n        let cast = &mut casts[idx];\n\n        let id = match &cast.target {\n            CastTarget::Nothing => {\n                self.backend.with_primary_renderer(|renderer| {\n                    if cast.dequeue_buffer_and_clear(renderer) {\n                        cast.last_frame_time = get_monotonic_time();\n                    }\n                });\n                return;\n            }\n            CastTarget::Output { output, .. } => {\n                if let Some(output) = output.upgrade() {\n                    self.niri.queue_redraw(&output);\n                }\n                return;\n            }\n            CastTarget::Window { id } => *id,\n        };\n\n        // Lack of partial borrowing strikes again...\n        let mut casts = mem::take(&mut self.niri.casting.casts);\n        let cast = &mut casts[idx];\n        let mut stop = false;\n        // Use a loop {} so we can break instead of early-return.\n        #[allow(clippy::never_loop)]\n        loop {\n            let mut windows = self.niri.layout.windows();\n            let Some((_, mapped)) = windows.find(|(_, mapped)| mapped.id().get() == id) else {\n                break;\n            };\n\n            // Use the cached output since it will be present even if the output was\n            // currently disconnected.\n            let Some(output) = self.niri.casting.mapped_cast_output.get(&mapped.window) else {\n                break;\n            };\n\n            let scale = Scale::from(output.current_scale().fractional_scale());\n            let bbox = mapped\n                .window\n                .bbox_with_popups()\n                .to_physical_precise_up(scale);\n\n            match cast.ensure_size(bbox.size) {\n                Ok(CastSizeChange::Ready) => (),\n                Ok(CastSizeChange::Pending) => break,\n                Err(err) => {\n                    warn!(\"error updating stream size, stopping screencast: {err:?}\");\n                    stop = true;\n                    break;\n                }\n            }\n\n            self.backend.with_primary_renderer(|renderer| {\n                let mut elements = Vec::new();\n                mapped.render_for_screen_cast(renderer, scale, &mut |elem| {\n                    elements.push(CastRenderElement::from(elem))\n                });\n\n                let mut pointer_elements = Vec::new();\n                let mut pointer_location = Point::default();\n\n                if self.niri.pointer_visibility.is_visible() {\n                    if let Some((pointer_pos, win_pos)) =\n                        self.niri.pointer_pos_for_window_cast(mapped)\n                    {\n                        // Pointer location must be relative to the screencast buffer.\n                        // - win_pos is the position of the main window surface in output-local\n                        //   coordinates\n                        // - bbox.loc moves us relative to the screencast buffer\n                        let buf_pos = win_pos + bbox.loc.to_f64().to_logical(scale);\n                        let output_pos =\n                            self.niri.global_space.output_geometry(output).unwrap().loc;\n                        pointer_location = pointer_pos - output_pos.to_f64() - buf_pos;\n\n                        let pos = buf_pos.to_physical_precise_round(scale).upscale(-1);\n                        self.niri.render_pointer(renderer, output, &mut |elem| {\n                            let elem =\n                                RelocateRenderElement::from_element(elem, pos, Relocate::Relative);\n                            pointer_elements.push(CastRenderElement::from(elem));\n                        });\n                    }\n                }\n                let cursor_data = CursorData::compute(&pointer_elements, pointer_location, scale);\n\n                if cast.dequeue_buffer_and_render(\n                    renderer,\n                    &elements,\n                    &cursor_data,\n                    bbox.size,\n                    scale,\n                ) {\n                    cast.last_frame_time = get_monotonic_time();\n                }\n            });\n\n            break;\n        }\n        let session_id = cast.session_id;\n        self.niri.casting.casts = casts;\n\n        if stop {\n            self.niri.stop_cast(session_id);\n        }\n    }\n\n    pub fn set_dynamic_cast_target(&mut self, target: CastTarget) {\n        let _span = tracy_client::span!(\"State::set_dynamic_cast_target\");\n\n        let mut refresh = None;\n        match &target {\n            // Leave refresh as is when clearing. Chances are, the next refresh will match it,\n            // then we'll avoid reconfiguring.\n            CastTarget::Nothing => (),\n            CastTarget::Output { output, .. } => {\n                if let Some(output) = output.upgrade() {\n                    refresh = Some(output.current_mode().unwrap().refresh as u32);\n                }\n            }\n            CastTarget::Window { id } => {\n                let mut windows = self.niri.layout.windows();\n                if let Some((_, mapped)) = windows.find(|(_, mapped)| mapped.id().get() == *id) {\n                    if let Some(output) = self.niri.casting.mapped_cast_output.get(&mapped.window) {\n                        refresh = Some(output.current_mode().unwrap().refresh as u32);\n                    }\n                }\n            }\n        }\n\n        let mut to_redraw = Vec::new();\n        let mut to_stop = Vec::new();\n        for cast in &mut self.niri.casting.casts {\n            if !cast.dynamic_target {\n                continue;\n            }\n\n            if let Some(refresh) = refresh {\n                if let Err(err) = cast.set_refresh(refresh) {\n                    warn!(\"error changing cast FPS: {err:?}\");\n                    to_stop.push(cast.session_id);\n                    continue;\n                }\n            }\n\n            cast.target = target.clone();\n            to_redraw.push(cast.stream_id);\n        }\n\n        for id in to_redraw {\n            self.redraw_cast(id);\n        }\n\n        // Start any pending dynamic casts if we have a real target.\n        if !matches!(target, CastTarget::Nothing) {\n            self.start_pending_dynamic_casts(&target);\n        }\n    }\n\n    fn start_pending_dynamic_casts(&mut self, target: &CastTarget) {\n        let pending = &self.niri.casting.pending_dynamic_casts;\n        if pending.is_empty() {\n            return;\n        }\n        debug!(\"starting {} pending dynamic cast(s)\", pending.len());\n\n        let _span = tracy_client::span!(\"State::start_pending_dynamic_casts\");\n\n        // We don't stop dynamic casts on missing output/window.\n        let (size, refresh) = match target {\n            CastTarget::Nothing => panic!(\"dynamic cast starting target must not be Nothing\"),\n            CastTarget::Output { output, .. } => {\n                let Some(output) = output.upgrade() else {\n                    return;\n                };\n                cast_params_for_output(&output)\n            }\n            CastTarget::Window { id } => {\n                let Some((size, refresh)) = self.niri.cast_params_for_window(*id) else {\n                    return;\n                };\n                (size, refresh)\n            }\n        };\n\n        let (gbm, render_formats) = match self.prepare_pw_cast() {\n            Ok(x) => x,\n            Err(err) => {\n                warn!(\"error starting pending screencasts: {err:?}\");\n                let mut ids = HashSet::new();\n                for pending in self.niri.casting.pending_dynamic_casts.drain(..) {\n                    ids.insert(pending.session_id);\n                }\n                for id in ids {\n                    self.niri.stop_cast(id);\n                }\n                return;\n            }\n        };\n        let pw = self.niri.casting.pipewire.as_ref().unwrap();\n\n        // Alpha is always true since the dynamic target can change between window & output.\n        let alpha = true;\n\n        // Start each pending cast.\n        let mut to_stop = HashSet::new();\n        for pending in self.niri.casting.pending_dynamic_casts.drain(..) {\n            let res = pw.start_cast(\n                gbm.clone(),\n                render_formats.clone(),\n                pending.session_id,\n                pending.stream_id,\n                target.clone(),\n                size,\n                refresh,\n                alpha,\n                pending.cursor_mode,\n                pending.signal_ctx,\n            );\n            match res {\n                Ok(mut cast) => {\n                    cast.dynamic_target = true;\n                    self.niri.casting.casts.push(cast);\n                }\n                Err(err) => {\n                    warn!(\"error starting pending screencast: {err:?}\");\n                    to_stop.insert(pending.session_id);\n                }\n            }\n        }\n\n        for session_id in to_stop {\n            self.niri.stop_cast(session_id);\n        }\n    }\n\n    pub fn on_screen_cast_msg(&mut self, msg: ScreenCastToNiri) {\n        match msg {\n            ScreenCastToNiri::StartCast {\n                session_id,\n                stream_id,\n                target,\n                cursor_mode,\n                signal_ctx,\n            } => {\n                let _span = tracy_client::span!(\"StartCast\");\n                let _span = debug_span!(\"StartCast\", %session_id, %stream_id).entered();\n\n                let (target, size, refresh, alpha) = match target {\n                    StreamTargetId::Output { name } => {\n                        let global_space = &self.niri.global_space;\n                        let output = global_space.outputs().find(|out| out.name() == name);\n                        let Some(output) = output else {\n                            warn!(\"error starting screencast: requested output is missing\");\n                            self.niri.stop_cast(session_id);\n                            return;\n                        };\n\n                        let (size, refresh) = cast_params_for_output(output);\n                        (CastTarget::output(output), size, refresh, false)\n                    }\n                    StreamTargetId::Window { id }\n                        if id == self.niri.casting.dynamic_cast_id_for_portal.get() =>\n                    {\n                        debug!(\"delaying dynamic cast until target is set\");\n                        self.niri.casting.pending_dynamic_casts.push(PendingCast {\n                            session_id,\n                            stream_id,\n                            cursor_mode,\n                            signal_ctx,\n                        });\n                        return;\n                    }\n                    StreamTargetId::Window { id } => {\n                        let Some((size, refresh)) = self.niri.cast_params_for_window(id) else {\n                            warn!(\"error starting screencast: requested window is missing\");\n                            self.niri.stop_cast(session_id);\n                            return;\n                        };\n                        (CastTarget::Window { id }, size, refresh, true)\n                    }\n                };\n\n                let (gbm, render_formats) = match self.prepare_pw_cast() {\n                    Ok(x) => x,\n                    Err(err) => {\n                        warn!(\"error starting screencast: {err:?}\");\n                        self.niri.stop_cast(session_id);\n                        return;\n                    }\n                };\n                let pw = self.niri.casting.pipewire.as_ref().unwrap();\n\n                let res = pw.start_cast(\n                    gbm,\n                    render_formats,\n                    session_id,\n                    stream_id,\n                    target,\n                    size,\n                    refresh,\n                    alpha,\n                    cursor_mode,\n                    signal_ctx,\n                );\n                match res {\n                    Ok(cast) => {\n                        self.niri.casting.casts.push(cast);\n                    }\n                    Err(err) => {\n                        warn!(\"error starting screencast: {err:?}\");\n                        self.niri.stop_cast(session_id);\n                    }\n                }\n            }\n            ScreenCastToNiri::StopCast { session_id } => self.niri.stop_cast(session_id),\n        }\n    }\n}\n\nimpl Niri {\n    pub fn refresh_mapped_cast_window_rules(&mut self) {\n        // O(N^2) but should be fine since there aren't many casts usually.\n        self.layout.with_windows_mut(|mapped, _| {\n            let id = mapped.id().get();\n            // Find regardless of cast.is_active.\n            let value = self\n                .casting\n                .casts\n                .iter()\n                .any(|cast| cast.target == (CastTarget::Window { id }));\n            mapped.set_is_window_cast_target(value);\n        });\n    }\n\n    pub fn refresh_mapped_cast_outputs(&mut self) {\n        let mut seen = HashSet::new();\n        let mut output_changed = vec![];\n\n        self.layout.with_windows(|mapped, output, _, _| {\n            seen.insert(mapped.window.clone());\n\n            let Some(output) = output else {\n                return;\n            };\n\n            match self.casting.mapped_cast_output.entry(mapped.window.clone()) {\n                Entry::Occupied(mut entry) => {\n                    if entry.get() != output {\n                        entry.insert(output.clone());\n                        output_changed.push((mapped.id(), output.clone()));\n                    }\n                }\n                Entry::Vacant(entry) => {\n                    entry.insert(output.clone());\n                }\n            }\n        });\n\n        self.casting\n            .mapped_cast_output\n            .retain(|win, _| seen.contains(win));\n\n        let mut to_stop = vec![];\n        for (id, out) in output_changed {\n            let refresh = out.current_mode().unwrap().refresh as u32;\n            let target = CastTarget::Window { id: id.get() };\n            for cast in self\n                .casting\n                .casts\n                .iter_mut()\n                .filter(|cast| cast.target == target)\n            {\n                if let Err(err) = cast.set_refresh(refresh) {\n                    warn!(\"error changing cast FPS: {err:?}\");\n                    to_stop.push(cast.session_id);\n                };\n            }\n        }\n\n        for session_id in to_stop {\n            self.stop_cast(session_id);\n        }\n    }\n\n    pub fn render_for_screen_cast(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        output: &Output,\n        target_presentation_time: Duration,\n    ) {\n        let _span = tracy_client::span!(\"Niri::render_for_screen_cast\");\n\n        let weak = output.downgrade();\n        let size = output.current_mode().unwrap().size;\n        let transform = output.current_transform();\n        let size = transform.transform_size(size);\n\n        let scale = Scale::from(output.current_scale().fractional_scale());\n\n        let mut elements = Vec::new();\n        let mut pointer = Vec::new();\n        let mut cursor_data = None;\n\n        let mut casts_to_stop = vec![];\n\n        let mut casts = mem::take(&mut self.casting.casts);\n        for cast in &mut casts {\n            if !cast.is_active() {\n                continue;\n            }\n\n            if !cast.target.matches_output(&weak) {\n                continue;\n            }\n\n            match cast.ensure_size(size) {\n                Ok(CastSizeChange::Ready) => (),\n                Ok(CastSizeChange::Pending) => continue,\n                Err(err) => {\n                    warn!(\"error updating stream size, stopping screencast: {err:?}\");\n                    casts_to_stop.push(cast.session_id);\n                }\n            }\n\n            if cast.check_time_and_schedule(output, target_presentation_time) {\n                continue;\n            }\n\n            if cursor_data.is_none() {\n                self.render_inner(\n                    renderer,\n                    output,\n                    false,\n                    RenderTarget::Screencast,\n                    &mut |elem| elements.push(elem.into()),\n                );\n\n                let mut pointer_pos = Point::default();\n                if self.pointer_visibility.is_visible() {\n                    let output_geo = self.global_space.output_geometry(output).unwrap().to_f64();\n                    let pointer_loc = self\n                        .tablet_cursor_location\n                        .unwrap_or_else(|| self.seat.get_pointer().unwrap().current_location());\n                    // Only render when the pointer is within the output. Otherwise, it will\n                    // happily appear anywhere outside the output video source in OBS.\n                    if output_geo.contains(pointer_loc) {\n                        pointer_pos = pointer_loc - output_geo.loc;\n                        self.render_pointer(renderer, output, &mut |elem| {\n                            pointer.push(elem.into())\n                        });\n                    }\n                }\n\n                cursor_data = Some(CursorData::compute(&pointer, pointer_pos, scale));\n            }\n            let cursor_data = cursor_data.as_ref().unwrap();\n\n            if cast.dequeue_buffer_and_render(renderer, &elements, cursor_data, size, scale) {\n                cast.last_frame_time = target_presentation_time;\n            }\n        }\n        self.casting.casts = casts;\n\n        for id in casts_to_stop {\n            self.stop_cast(id);\n        }\n    }\n\n    pub fn render_windows_for_screen_cast(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        output: &Output,\n        target_presentation_time: Duration,\n    ) {\n        let _span = tracy_client::span!(\"Niri::render_windows_for_screen_cast\");\n\n        let scale = Scale::from(output.current_scale().fractional_scale());\n\n        let mut casts_to_stop = vec![];\n\n        let mut casts = mem::take(&mut self.casting.casts);\n        for cast in &mut casts {\n            if !cast.is_active() {\n                continue;\n            }\n\n            let CastTarget::Window { id } = cast.target else {\n                continue;\n            };\n\n            let mut windows = self.layout.windows_for_output(output);\n            let Some(mapped) = windows.find(|win| win.id().get() == id) else {\n                continue;\n            };\n\n            let bbox = mapped\n                .window\n                .bbox_with_popups()\n                .to_physical_precise_up(scale);\n\n            match cast.ensure_size(bbox.size) {\n                Ok(CastSizeChange::Ready) => (),\n                Ok(CastSizeChange::Pending) => continue,\n                Err(err) => {\n                    warn!(\"error updating stream size, stopping screencast: {err:?}\");\n                    casts_to_stop.push(cast.session_id);\n                }\n            }\n\n            if cast.check_time_and_schedule(output, target_presentation_time) {\n                continue;\n            }\n\n            let mut elements = Vec::new();\n            mapped.render_for_screen_cast(renderer, scale, &mut |elem| {\n                elements.push(CastRenderElement::from(elem))\n            });\n\n            let mut pointer_elements = Vec::new();\n            let mut pointer_location = Point::default();\n\n            if self.pointer_visibility.is_visible() {\n                if let Some((pointer_pos, win_pos)) = self.pointer_pos_for_window_cast(mapped) {\n                    // Pointer location must be relative to the screencast buffer.\n                    // - win_pos is the position of the main window surface in output-local\n                    //   coordinates\n                    // - bbox.loc moves us relative to the screencast buffer\n                    let buf_pos = win_pos + bbox.loc.to_f64().to_logical(scale);\n                    let output_pos = self.global_space.output_geometry(output).unwrap().loc;\n                    pointer_location = pointer_pos - output_pos.to_f64() - buf_pos;\n\n                    let pos = buf_pos.to_physical_precise_round(scale).upscale(-1);\n                    self.render_pointer(renderer, output, &mut |elem| {\n                        let elem =\n                            RelocateRenderElement::from_element(elem, pos, Relocate::Relative);\n                        pointer_elements.push(CastRenderElement::from(elem));\n                    });\n                }\n            }\n            let cursor_data = CursorData::compute(&pointer_elements, pointer_location, scale);\n\n            if cast.dequeue_buffer_and_render(renderer, &elements, &cursor_data, bbox.size, scale) {\n                cast.last_frame_time = target_presentation_time;\n            }\n        }\n        self.casting.casts = casts;\n\n        for id in casts_to_stop {\n            self.stop_cast(id);\n        }\n    }\n\n    pub fn stop_cast(&mut self, session_id: CastSessionId) {\n        let _span = tracy_client::span!(\"Niri::stop_cast\");\n        let _span = debug_span!(\"stop_cast\", %session_id).entered();\n\n        self.casting\n            .pending_dynamic_casts\n            .retain(|p| p.session_id != session_id);\n\n        for i in (0..self.casting.casts.len()).rev() {\n            let cast = &self.casting.casts[i];\n            if cast.session_id != session_id {\n                continue;\n            }\n\n            let cast = self.casting.casts.swap_remove(i);\n            if let Err(err) = cast.stream.disconnect() {\n                warn!(\"error disconnecting stream: {err:?}\");\n            }\n        }\n\n        let dbus = &self.dbus.as_ref().unwrap();\n        let server = dbus.conn_screen_cast.as_ref().unwrap().object_server();\n        let path = format!(\"/org/gnome/Mutter/ScreenCast/Session/u{}\", session_id.get());\n        if let Ok(iface) = server.interface::<_, mutter_screen_cast::Session>(path) {\n            let _span = tracy_client::span!(\"invoking Session::stop\");\n\n            async_io::block_on(async move {\n                iface\n                    .get()\n                    .stop(server.inner(), iface.signal_emitter().clone())\n                    .await\n            });\n        }\n    }\n\n    pub fn stop_casts_for_target(&mut self, target: CastTarget) {\n        let _span = tracy_client::span!(\"Niri::stop_casts_for_target\");\n\n        // This is O(N^2) but it shouldn't be a problem I think.\n        let mut saw_dynamic = false;\n        let mut ids = Vec::new();\n        for cast in &self.casting.casts {\n            if cast.target != target {\n                continue;\n            }\n\n            if cast.dynamic_target {\n                saw_dynamic = true;\n                continue;\n            }\n\n            ids.push(cast.session_id);\n        }\n\n        for id in ids {\n            self.stop_cast(id);\n        }\n\n        // We don't stop dynamic casts, instead we switch them to Nothing.\n        if saw_dynamic {\n            self.event_loop\n                .insert_idle(|state| state.set_dynamic_cast_target(CastTarget::Nothing));\n        }\n    }\n\n    fn cast_params_for_window(&self, window_id: u64) -> Option<(Size<i32, Physical>, u32)> {\n        let (_, mapped) = self\n            .layout\n            .windows()\n            .find(|(_, m)| m.id().get() == window_id)?;\n        let output = self.casting.mapped_cast_output.get(&mapped.window)?;\n        let scale = Scale::from(output.current_scale().fractional_scale());\n        let bbox = mapped\n            .window\n            .bbox_with_popups()\n            .to_physical_precise_up(scale);\n        let refresh = output.current_mode().unwrap().refresh as u32;\n        Some((bbox.size, refresh))\n    }\n}\n\nfn cast_params_for_output(output: &Output) -> (Size<i32, Physical>, u32) {\n    let mode = output.current_mode().unwrap();\n    let transform = output.current_transform();\n    let size = transform.transform_size(mode.size);\n    let refresh = mode.refresh as u32;\n    (size, refresh)\n}\n\nniri_render_elements! {\n    CastRenderElement<R> => {\n        Output = OutputRenderElements<R>,\n        Window = WindowCastRenderElements<R>,\n        Pointer = PointerRenderElements<R>,\n        RelocatedPointer = RelocateRenderElement<PointerRenderElements<R>>,\n    }\n}\n"
  },
  {
    "path": "src/screencasting/pw_utils.rs",
    "content": "use std::cell::RefCell;\nuse std::cmp::min;\nuse std::collections::HashMap;\nuse std::io::Cursor;\nuse std::iter::zip;\nuse std::os::fd::{AsFd, AsRawFd, BorrowedFd};\nuse std::ptr::NonNull;\nuse std::rc::Rc;\nuse std::time::Duration;\nuse std::{mem, slice};\n\nuse anyhow::Context as _;\nuse calloop::timer::{TimeoutAction, Timer};\nuse calloop::RegistrationToken;\nuse pipewire::context::ContextRc;\nuse pipewire::core::{CoreRc, PW_ID_CORE};\nuse pipewire::main_loop::MainLoopRc;\nuse pipewire::properties::PropertiesBox;\nuse pipewire::spa::buffer::DataType;\nuse pipewire::spa::param::format::{FormatProperties, MediaSubtype, MediaType};\nuse pipewire::spa::param::format_utils::parse_format;\nuse pipewire::spa::param::video::{VideoFormat, VideoInfoRaw};\nuse pipewire::spa::param::ParamType;\nuse pipewire::spa::pod::deserialize::PodDeserializer;\nuse pipewire::spa::pod::serialize::PodSerializer;\nuse pipewire::spa::pod::{self, ChoiceValue, Pod, PodPropFlags, Property, PropertyFlags};\nuse pipewire::spa::sys::*;\nuse pipewire::spa::utils::{\n    Choice, ChoiceEnum, ChoiceFlags, Direction, Fraction, Rectangle, SpaTypes,\n};\nuse pipewire::spa::{self};\nuse pipewire::stream::{Stream, StreamFlags, StreamListener, StreamRc, StreamState};\nuse pipewire::sys::{pw_buffer, pw_check_library_version, pw_stream_queue_buffer};\nuse smithay::backend::allocator::dmabuf::{AsDmabuf, Dmabuf};\nuse smithay::backend::allocator::format::FormatSet;\nuse smithay::backend::allocator::gbm::{GbmBuffer, GbmBufferFlags, GbmDevice};\nuse smithay::backend::allocator::{Format, Fourcc};\nuse smithay::backend::drm::DrmDeviceFd;\nuse smithay::backend::renderer::damage::OutputDamageTracker;\nuse smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};\nuse smithay::backend::renderer::element::{Element, RenderElement};\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::backend::renderer::sync::SyncPoint;\nuse smithay::backend::renderer::ExportMem;\nuse smithay::output::{Output, OutputModeSource};\nuse smithay::reexports::calloop::generic::Generic;\nuse smithay::reexports::calloop::{Interest, LoopHandle, Mode, PostAction};\nuse smithay::reexports::gbm::Modifier;\nuse smithay::utils::{Logical, Physical, Point, Scale, Size, Transform};\nuse zbus::object_server::SignalEmitter;\n\nuse crate::dbus::mutter_screen_cast::{self, CursorMode};\nuse crate::niri::{CastTarget, State};\nuse crate::render_helpers::{\n    clear_dmabuf, encompassing_geo, render_and_download, render_to_dmabuf,\n};\nuse crate::screencasting::CastRenderElement;\nuse crate::utils::{get_monotonic_time, CastSessionId, CastStreamId};\n\n// Give a 0.1 ms allowance for presentation time errors.\nconst CAST_DELAY_ALLOWANCE: Duration = Duration::from_micros(100);\n\nconst CURSOR_FORMAT: spa_video_format = SPA_VIDEO_FORMAT_BGRA;\nconst CURSOR_BPP: u32 = 4;\nconst CURSOR_WIDTH: u32 = 384;\nconst CURSOR_HEIGHT: u32 = 384;\nconst CURSOR_BITMAP_SIZE: usize = (CURSOR_WIDTH * CURSOR_HEIGHT * CURSOR_BPP) as usize;\nconst CURSOR_META_SIZE: usize =\n    mem::size_of::<spa_meta_cursor>() + mem::size_of::<spa_meta_bitmap>() + CURSOR_BITMAP_SIZE;\nconst BITMAP_META_OFFSET: usize = mem::size_of::<spa_meta_cursor>();\nconst BITMAP_DATA_OFFSET: usize = mem::size_of::<spa_meta_bitmap>();\n\npub struct PipeWire {\n    _context: ContextRc,\n    pub core: CoreRc,\n    pub token: RegistrationToken,\n    event_loop: LoopHandle<'static, State>,\n    to_niri: calloop::channel::Sender<PwToNiri>,\n}\n\npub enum PwToNiri {\n    StopCast { session_id: CastSessionId },\n    Redraw { stream_id: CastStreamId },\n    FatalError,\n}\n\npub struct Cast {\n    event_loop: LoopHandle<'static, State>,\n    pub session_id: CastSessionId,\n    pub stream_id: CastStreamId,\n    // Listener is dropped before Stream to prevent a use-after-free.\n    _listener: StreamListener<()>,\n    pub stream: StreamRc,\n    pub target: CastTarget,\n    pub dynamic_target: bool,\n    formats: FormatSet,\n    offer_alpha: bool,\n    cursor_mode: CursorMode,\n    pub last_frame_time: Duration,\n    scheduled_redraw: Option<RegistrationToken>,\n    // Incremented once per successful frame, stored in buffer meta.\n    sequence_counter: u64,\n    inner: Rc<RefCell<CastInner>>,\n}\n\n/// Mutable `Cast` state shared with PipeWire callbacks.\n#[derive(Debug)]\nstruct CastInner {\n    is_active: bool,\n    node_id: Option<u32>,\n    state: CastState,\n    refresh: u32,\n    min_time_between_frames: Duration,\n    dmabufs: HashMap<i64, Dmabuf>,\n    /// Buffers dequeued from PipeWire in process of rendering.\n    ///\n    /// This is an ordered list of buffers that we started rendering to and waiting for the\n    /// rendering to complete. The completion can be checked from the `SyncPoint`s. The buffers are\n    /// stored in order from oldest to newest, and the same ordering should be preserved when\n    /// submitting completed buffers to PipeWire.\n    rendering_buffers: Vec<(NonNull<pw_buffer>, SyncPoint)>,\n}\n\n#[allow(clippy::large_enum_variant)]\n#[derive(Debug)]\nenum CastState {\n    ResizePending {\n        pending_size: Size<u32, Physical>,\n    },\n    ConfirmationPending {\n        size: Size<u32, Physical>,\n        alpha: bool,\n        modifier: Modifier,\n        plane_count: i32,\n    },\n    Ready {\n        size: Size<u32, Physical>,\n        alpha: bool,\n        modifier: Modifier,\n        plane_count: i32,\n        // Lazily-initialized to keep the initialization to a single place.\n        damage_tracker: Option<OutputDamageTracker>,\n        cursor_damage_tracker: Option<OutputDamageTracker>,\n        last_cursor_location: Option<Point<i32, Physical>>,\n    },\n}\n\n#[derive(PartialEq, Eq)]\npub enum CastSizeChange {\n    Ready,\n    Pending,\n}\n\n/// Data for drawing a cursor either as metadata or embedded.\n///\n/// We have weird borrowed references here in order to support both metadata and embedded cases.\n/// The cursor damage tracker needs a slice of impl Element at (0, 0), so we pass it `relocated`\n/// (luckily, &impl Element also impls Element). Then, if we need to embed the cursor, we chain the\n/// elements to the main video buffer elements, so we need the same type. We use `original` for\n/// this; `E` is expected to match the type of the main video buffer elements.\n#[derive(Debug)]\npub struct CursorData<'a, E> {\n    /// Cursor elements at their original location.\n    original: &'a [E],\n    /// Cursor elements relocated to (0, 0).\n    relocated: Vec<RelocateRenderElement<&'a E>>,\n    /// Location of the cursor's hotspot in the video buffer.\n    location: Point<i32, Physical>,\n    /// Location of the cursor's hotspot on the cursor bitmap.\n    hotspot: Point<i32, Physical>,\n    /// Size of the elements' encompassing geo.\n    size: Size<i32, Physical>,\n    /// Scale the elements should be rendered at.\n    scale: Scale<f64>,\n}\n\nimpl<'a, E: Element> CursorData<'a, E> {\n    pub fn compute(elements: &'a [E], location: Point<f64, Logical>, scale: Scale<f64>) -> Self {\n        let location = location.to_physical_precise_round(scale);\n\n        let geo = encompassing_geo(scale, elements.iter());\n        let relocated = Vec::from_iter(elements.iter().map(|elem| {\n            RelocateRenderElement::from_element(elem, geo.loc.upscale(-1), Relocate::Relative)\n        }));\n\n        Self {\n            original: elements,\n            relocated,\n            location,\n            hotspot: location - geo.loc,\n            size: geo.size,\n            scale,\n        }\n    }\n}\n\nmacro_rules! make_params {\n    ($params:ident, $formats:expr, $size:expr, $refresh:expr, $alpha:expr) => {\n        let mut b1 = Vec::new();\n        let mut b2 = Vec::new();\n\n        let o1 = make_video_params($formats, $size, $refresh, false);\n        let pod1 = make_pod(&mut b1, o1);\n\n        let mut p1;\n        let mut p2;\n        $params = if $alpha {\n            let o2 = make_video_params($formats, $size, $refresh, true);\n            p2 = [pod1, make_pod(&mut b2, o2)];\n            &mut p2[..]\n        } else {\n            p1 = [pod1];\n            &mut p1[..]\n        };\n    };\n}\n\nimpl PipeWire {\n    pub fn new(\n        event_loop: LoopHandle<'static, State>,\n        to_niri: calloop::channel::Sender<PwToNiri>,\n    ) -> anyhow::Result<Self> {\n        let main_loop = MainLoopRc::new(None).context(\"error creating MainLoop\")?;\n        let context = ContextRc::new(&main_loop, None).context(\"error creating Context\")?;\n        let core = context.connect_rc(None).context(\"error creating Core\")?;\n\n        let to_niri_ = to_niri.clone();\n        let listener = core\n            .add_listener_local()\n            .error(move |id, seq, res, message| {\n                warn!(id, seq, res, message, \"pw error\");\n\n                // Reset PipeWire on connection errors.\n                if id == PW_ID_CORE && res == -32 {\n                    if let Err(err) = to_niri_.send(PwToNiri::FatalError) {\n                        warn!(\"error sending FatalError to niri: {err:?}\");\n                    }\n                }\n            })\n            .register();\n        mem::forget(listener);\n\n        struct AsFdWrapper(MainLoopRc);\n        impl AsFd for AsFdWrapper {\n            fn as_fd(&self) -> BorrowedFd<'_> {\n                self.0.loop_().fd()\n            }\n        }\n        let generic = Generic::new(AsFdWrapper(main_loop), Interest::READ, Mode::Level);\n        let token = event_loop\n            .insert_source(generic, move |_, wrapper, _| {\n                let _span = tracy_client::span!(\"pipewire iteration\");\n                wrapper.0.loop_().iterate(Duration::ZERO);\n                Ok(PostAction::Continue)\n            })\n            .unwrap();\n\n        Ok(Self {\n            _context: context,\n            core,\n            token,\n            event_loop,\n            to_niri,\n        })\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn start_cast(\n        &self,\n        gbm: GbmDevice<DrmDeviceFd>,\n        formats: FormatSet,\n        session_id: CastSessionId,\n        stream_id: CastStreamId,\n        target: CastTarget,\n        size: Size<i32, Physical>,\n        refresh: u32,\n        alpha: bool,\n        mut cursor_mode: CursorMode,\n        signal_ctx: SignalEmitter<'static>,\n    ) -> anyhow::Result<Cast> {\n        let _span = tracy_client::span!(\"PipeWire::start_cast\");\n\n        let to_niri_ = self.to_niri.clone();\n        let stop_cast = move || {\n            if let Err(err) = to_niri_.send(PwToNiri::StopCast { session_id }) {\n                warn!(%session_id, \"error sending StopCast to niri: {err:?}\");\n            }\n        };\n        let to_niri_ = self.to_niri.clone();\n        let redraw = move || {\n            if let Err(err) = to_niri_.send(PwToNiri::Redraw { stream_id }) {\n                warn!(%stream_id, \"error sending Redraw to niri: {err:?}\");\n            }\n        };\n        let redraw_ = redraw.clone();\n\n        let stream = StreamRc::new(\n            self.core.clone(),\n            \"niri-screen-cast-src\",\n            PropertiesBox::new(),\n        )\n        .context(\"error creating Stream\")?;\n\n        if cursor_mode == CursorMode::Metadata && !pw_version_supports_cursor_metadata() {\n            debug!(\n                \"metadata cursor mode requested, but PipeWire is too old (need >= 1.4.8); \\\n                 switching to embedded cursor\"\n            );\n            cursor_mode = CursorMode::Embedded;\n        }\n\n        let pending_size = Size::from((size.w as u32, size.h as u32));\n\n        // Like in good old wayland-rs times...\n        let inner = Rc::new(RefCell::new(CastInner {\n            is_active: false,\n            node_id: None,\n            state: CastState::ResizePending { pending_size },\n            refresh,\n            min_time_between_frames: Duration::ZERO,\n            dmabufs: HashMap::new(),\n            rendering_buffers: Vec::new(),\n        }));\n\n        let listener = stream\n            .add_local_listener_with_user_data(())\n            .state_changed({\n                let inner = inner.clone();\n                let stop_cast = stop_cast.clone();\n                move |stream, (), old, new| {\n                    let _span = debug_span!(\"state_changed\", %stream_id).entered();\n                    debug!(\"{old:?} -> {new:?}\");\n                    let mut inner = inner.borrow_mut();\n\n                    match new {\n                        StreamState::Paused => {\n                            if inner.node_id.is_none() {\n                                let id = stream.node_id();\n                                inner.node_id = Some(id);\n                                debug!(\"sending signal with {id}\");\n\n                                let _span = tracy_client::span!(\"sending PipeWireStreamAdded\");\n                                async_io::block_on(async {\n                                    let res = mutter_screen_cast::Stream::pipe_wire_stream_added(\n                                        &signal_ctx,\n                                        id,\n                                    )\n                                    .await;\n\n                                    if let Err(err) = res {\n                                        warn!(\"error sending PipeWireStreamAdded: {err:?}\");\n                                        stop_cast();\n                                    }\n                                });\n                            }\n\n                            inner.is_active = false;\n                        }\n                        StreamState::Error(_) => {\n                            if inner.is_active {\n                                inner.is_active = false;\n                                stop_cast();\n                            }\n                        }\n                        StreamState::Unconnected => (),\n                        StreamState::Connecting => (),\n                        StreamState::Streaming => {\n                            inner.is_active = true;\n                            redraw();\n                        }\n                    }\n                }\n            })\n            .param_changed({\n                let inner = inner.clone();\n                let stop_cast = stop_cast.clone();\n                let gbm = gbm.clone();\n                let formats = formats.clone();\n                move |stream, (), id, pod| {\n                    let id = ParamType::from_raw(id);\n                    trace!(%stream_id, ?id, \"param_changed\");\n                    let mut inner = inner.borrow_mut();\n                    let inner = &mut *inner;\n\n                    if id != ParamType::Format {\n                        return;\n                    }\n\n                    let _span = debug_span!(\"param_changed\", %stream_id).entered();\n\n                    let Some(pod) = pod else { return };\n\n                    let (m_type, m_subtype) = match parse_format(pod) {\n                        Ok(x) => x,\n                        Err(err) => {\n                            warn!(\"error parsing format: {err:?}\");\n                            return;\n                        }\n                    };\n\n                    if m_type != MediaType::Video || m_subtype != MediaSubtype::Raw {\n                        return;\n                    }\n\n                    let mut format = VideoInfoRaw::new();\n                    format.parse(pod).unwrap();\n                    debug!(\"got format = {format:?}\");\n\n                    let format_size = Size::from((format.size().width, format.size().height));\n\n                    let state = &mut inner.state;\n                    if format_size != state.expected_format_size() {\n                        if !matches!(&*state, CastState::ResizePending { .. }) {\n                            warn!(\"wrong size, but we're not resizing\");\n                            stop_cast();\n                            return;\n                        }\n\n                        debug!(\"wrong size, waiting\");\n                        return;\n                    }\n\n                    let format_has_alpha = format.format() == VideoFormat::BGRA;\n                    let fourcc = if format_has_alpha {\n                        Fourcc::Argb8888\n                    } else {\n                        Fourcc::Xrgb8888\n                    };\n\n                    let max_frame_rate = format.max_framerate();\n                    let min_frame_time = Duration::from_micros(\n                        1_000_000 * u64::from(max_frame_rate.denom) / u64::from(max_frame_rate.num),\n                    );\n                    inner.min_time_between_frames = min_frame_time;\n\n                    let object = pod.as_object().unwrap();\n                    let Some(prop_modifier) =\n                        object.find_prop(spa::utils::Id(FormatProperties::VideoModifier.0))\n                    else {\n                        warn!(\"modifier prop missing\");\n                        stop_cast();\n                        return;\n                    };\n\n                    if prop_modifier.flags().contains(PodPropFlags::DONT_FIXATE) {\n                        debug!(\"fixating the modifier\");\n\n                        let pod_modifier = prop_modifier.value();\n                        let Ok((_, modifiers)) = PodDeserializer::deserialize_from::<Choice<i64>>(\n                            pod_modifier.as_bytes(),\n                        ) else {\n                            warn!(\"wrong modifier property type\");\n                            stop_cast();\n                            return;\n                        };\n\n                        let ChoiceEnum::Enum { alternatives, .. } = modifiers.1 else {\n                            warn!(\"wrong modifier choice type\");\n                            stop_cast();\n                            return;\n                        };\n\n                        let (modifier, plane_count) = match find_preferred_modifier(\n                            &gbm,\n                            format_size,\n                            fourcc,\n                            alternatives,\n                        ) {\n                            Ok(x) => x,\n                            Err(err) => {\n                                warn!(\"couldn't find preferred modifier: {err:?}\");\n                                stop_cast();\n                                return;\n                            }\n                        };\n\n                        debug!(\n                            \"allocation successful \\\n                             (modifier={modifier:?}, plane_count={plane_count}), \\\n                             moving to confirmation pending\"\n                        );\n\n                        *state = CastState::ConfirmationPending {\n                            size: format_size,\n                            alpha: format_has_alpha,\n                            modifier,\n                            plane_count: plane_count as i32,\n                        };\n\n                        let fixated_format = FormatSet::from_iter([Format {\n                            code: fourcc,\n                            modifier,\n                        }]);\n\n                        let mut b1 = Vec::new();\n                        let mut b2 = Vec::new();\n\n                        let o1 = make_video_params(\n                            &fixated_format,\n                            format_size,\n                            inner.refresh,\n                            format_has_alpha,\n                        );\n                        let pod1 = make_pod(&mut b1, o1);\n\n                        let o2 = make_video_params(\n                            &formats,\n                            format_size,\n                            inner.refresh,\n                            format_has_alpha,\n                        );\n                        let mut params = [pod1, make_pod(&mut b2, o2)];\n\n                        if let Err(err) = stream.update_params(&mut params) {\n                            warn!(\"error updating stream params: {err:?}\");\n                            stop_cast();\n                        }\n\n                        return;\n                    }\n\n                    // Verify that alpha and modifier didn't change.\n                    let plane_count = match &*state {\n                        CastState::ConfirmationPending {\n                            size,\n                            alpha,\n                            modifier,\n                            plane_count,\n                        }\n                        | CastState::Ready {\n                            size,\n                            alpha,\n                            modifier,\n                            plane_count,\n                            ..\n                        } if *alpha == format_has_alpha\n                            && *modifier == Modifier::from(format.modifier()) =>\n                        {\n                            let size = *size;\n                            let alpha = *alpha;\n                            let modifier = *modifier;\n                            let plane_count = *plane_count;\n\n                            let (damage_tracker, cursor_damage_tracker) =\n                                if let CastState::Ready {\n                                    damage_tracker,\n                                    cursor_damage_tracker,\n                                    ..\n                                } = &mut *state\n                                {\n                                    (damage_tracker.take(), cursor_damage_tracker.take())\n                                } else {\n                                    (None, None)\n                                };\n\n                            debug!(\"moving to ready state\");\n\n                            *state = CastState::Ready {\n                                size,\n                                alpha,\n                                modifier,\n                                plane_count,\n                                damage_tracker,\n                                cursor_damage_tracker,\n                                last_cursor_location: None,\n                            };\n\n                            plane_count\n                        }\n                        _ => {\n                            // We're negotiating a single modifier, or alpha or modifier changed,\n                            // so we need to do a test allocation.\n                            let (modifier, plane_count) = match find_preferred_modifier(\n                                &gbm,\n                                format_size,\n                                fourcc,\n                                vec![format.modifier() as i64],\n                            ) {\n                                Ok(x) => x,\n                                Err(err) => {\n                                    warn!(\"test allocation failed: {err:?}\");\n                                    stop_cast();\n                                    return;\n                                }\n                            };\n\n                            debug!(\n                                \"allocation successful \\\n                                 (modifier={modifier:?}, plane_count={plane_count}), \\\n                                 moving to ready\"\n                            );\n\n                            *state = CastState::Ready {\n                                size: format_size,\n                                alpha: format_has_alpha,\n                                modifier,\n                                plane_count: plane_count as i32,\n                                damage_tracker: None,\n                                cursor_damage_tracker: None,\n                                last_cursor_location: None,\n                            };\n\n                            plane_count as i32\n                        }\n                    };\n\n                    // const BPP: u32 = 4;\n                    // let stride = format.size().width * BPP;\n                    // let size = stride * format.size().height;\n\n                    let o1 = pod::object!(\n                        SpaTypes::ObjectParamBuffers,\n                        ParamType::Buffers,\n                        Property::new(\n                            SPA_PARAM_BUFFERS_buffers,\n                            pod::Value::Choice(ChoiceValue::Int(Choice(\n                                ChoiceFlags::empty(),\n                                ChoiceEnum::Range {\n                                    default: 8,\n                                    min: 2,\n                                    max: 16\n                                }\n                            ))),\n                        ),\n                        Property::new(SPA_PARAM_BUFFERS_blocks, pod::Value::Int(plane_count)),\n                        Property::new(\n                            SPA_PARAM_BUFFERS_dataType,\n                            pod::Value::Choice(ChoiceValue::Int(Choice(\n                                ChoiceFlags::empty(),\n                                ChoiceEnum::Flags {\n                                    default: 1 << DataType::DmaBuf.as_raw(),\n                                    flags: vec![1 << DataType::DmaBuf.as_raw()],\n                                },\n                            ))),\n                        ),\n                    );\n\n                    let o2 = pod::object!(\n                        SpaTypes::ObjectParamMeta,\n                        ParamType::Meta,\n                        Property::new(\n                            SPA_PARAM_META_type,\n                            pod::Value::Id(spa::utils::Id(SPA_META_Header))\n                        ),\n                        Property::new(\n                            SPA_PARAM_META_size,\n                            pod::Value::Int(size_of::<spa_meta_header>() as i32)\n                        ),\n                    );\n                    let mut b1 = vec![];\n                    let mut b2 = vec![];\n                    let mut params = vec![make_pod(&mut b1, o1), make_pod(&mut b2, o2)];\n\n                    let mut b_cursor = vec![];\n                    if cursor_mode == CursorMode::Metadata {\n                        let o_cursor = pod::object!(\n                            SpaTypes::ObjectParamMeta,\n                            ParamType::Meta,\n                            Property::new(\n                                SPA_PARAM_META_type,\n                                pod::Value::Id(spa::utils::Id(SPA_META_Cursor))\n                            ),\n                            Property::new(\n                                SPA_PARAM_META_size,\n                                pod::Value::Int(CURSOR_META_SIZE as i32)\n                            ),\n                        );\n                        params.push(make_pod(&mut b_cursor, o_cursor));\n                    }\n\n                    if let Err(err) = stream.update_params(&mut params) {\n                        warn!(\"error updating stream params: {err:?}\");\n                        stop_cast();\n                    }\n                }\n            })\n            .add_buffer({\n                let inner = inner.clone();\n                let stop_cast = stop_cast.clone();\n                move |stream, (), buffer| {\n                    let _span = debug_span!(\"add_buffer\", %stream_id).entered();\n                    let mut inner = inner.borrow_mut();\n\n                    let (size, alpha, modifier) = if let CastState::Ready {\n                        size,\n                        alpha,\n                        modifier,\n                        ..\n                    } = &inner.state\n                    {\n                        (*size, *alpha, *modifier)\n                    } else {\n                        trace!(\"add_buffer, but not ready yet\");\n                        return;\n                    };\n\n                    trace!(\"size={size:?}, alpha={alpha}, modifier={modifier:?}\");\n\n                    unsafe {\n                        let spa_buffer = (*buffer).buffer;\n\n                        let fourcc = if alpha {\n                            Fourcc::Argb8888\n                        } else {\n                            Fourcc::Xrgb8888\n                        };\n\n                        let dmabuf = match allocate_dmabuf(&gbm, size, fourcc, modifier) {\n                            Ok(dmabuf) => dmabuf,\n                            Err(err) => {\n                                warn!(\"error allocating dmabuf: {err:?}\");\n                                stop_cast();\n                                return;\n                            }\n                        };\n\n                        let plane_count = dmabuf.num_planes();\n                        assert_eq!((*spa_buffer).n_datas as usize, plane_count);\n\n                        for (i, (fd, (stride, offset))) in\n                            zip(dmabuf.handles(), zip(dmabuf.strides(), dmabuf.offsets()))\n                                .enumerate()\n                        {\n                            let spa_data = (*spa_buffer).datas.add(i);\n                            assert!((*spa_data).type_ & (1 << DataType::DmaBuf.as_raw()) > 0);\n\n                            (*spa_data).type_ = DataType::DmaBuf.as_raw();\n\n                            // With DMA-BUFs, consumers should ignore the maxsize field, and\n                            // producers are allowed to set it to 0.\n                            //\n                            // https://docs.pipewire.org/page_dma_buf.html\n                            (*spa_data).maxsize = 1;\n                            (*spa_data).fd = fd.as_raw_fd() as i64;\n                            (*spa_data).flags = SPA_DATA_FLAG_READWRITE;\n\n                            let chunk = (*spa_data).chunk;\n                            (*chunk).stride = stride as i32;\n                            (*chunk).offset = offset;\n\n                            trace!(\n                                \"pw buffer plane: fd={}, stride={stride}, offset={offset}\",\n                                (*spa_data).fd\n                            );\n                        }\n\n                        let fd = (*(*spa_buffer).datas).fd;\n                        assert!(inner.dmabufs.insert(fd, dmabuf).is_none());\n                    }\n\n                    // During size re-negotiation, the stream sometimes just keeps running, in\n                    // which case we may need to force a redraw once we got a newly sized buffer.\n                    if inner.dmabufs.len() == 1 && stream.state() == StreamState::Streaming {\n                        redraw_();\n                    }\n                }\n            })\n            .remove_buffer({\n                let inner = inner.clone();\n                move |_stream, (), buffer| {\n                    trace!(%stream_id, \"remove_buffer\");\n                    let mut inner = inner.borrow_mut();\n\n                    inner\n                        .rendering_buffers\n                        .retain(|(buf, _)| buf.as_ptr() != buffer);\n\n                    unsafe {\n                        let spa_buffer = (*buffer).buffer;\n                        let spa_data = (*spa_buffer).datas;\n                        assert!((*spa_buffer).n_datas > 0);\n\n                        let fd = (*spa_data).fd;\n                        inner.dmabufs.remove(&fd);\n                    }\n                }\n            })\n            .register()\n            .unwrap();\n\n        trace!(\n            %stream_id,\n            \"starting pw stream with size={pending_size:?}, refresh={refresh:?}\"\n        );\n\n        let params;\n        make_params!(params, &formats, pending_size, refresh, alpha);\n        stream\n            .connect(\n                Direction::Output,\n                None,\n                StreamFlags::DRIVER | StreamFlags::ALLOC_BUFFERS,\n                params,\n            )\n            .context(\"error connecting stream\")?;\n\n        let cast = Cast {\n            event_loop: self.event_loop.clone(),\n            session_id,\n            stream_id,\n            stream,\n            _listener: listener,\n            target,\n            dynamic_target: false,\n            formats,\n            offer_alpha: alpha,\n            cursor_mode,\n            last_frame_time: Duration::ZERO,\n            scheduled_redraw: None,\n            sequence_counter: 0,\n            inner,\n        };\n        Ok(cast)\n    }\n}\n\nimpl Cast {\n    pub fn is_active(&self) -> bool {\n        self.inner.borrow().is_active\n    }\n\n    pub fn node_id(&self) -> Option<u32> {\n        self.inner.borrow().node_id\n    }\n\n    pub fn ensure_size(&self, size: Size<i32, Physical>) -> anyhow::Result<CastSizeChange> {\n        let mut inner = self.inner.borrow_mut();\n\n        let new_size = Size::from((size.w as u32, size.h as u32));\n\n        let state = &mut inner.state;\n        if matches!(state, CastState::Ready { size, .. } if *size == new_size) {\n            return Ok(CastSizeChange::Ready);\n        }\n\n        if state.pending_size() == Some(new_size) {\n            debug!(\"stream size still hasn't changed, skipping frame\");\n            return Ok(CastSizeChange::Pending);\n        }\n\n        let _span = tracy_client::span!(\"Cast::ensure_size\");\n        debug!(\"cast size changed, updating stream size\");\n\n        *state = CastState::ResizePending {\n            pending_size: new_size,\n        };\n\n        let params;\n        make_params!(\n            params,\n            &self.formats,\n            new_size,\n            inner.refresh,\n            self.offer_alpha\n        );\n        self.stream\n            .update_params(params)\n            .context(\"error updating stream params\")?;\n\n        Ok(CastSizeChange::Pending)\n    }\n\n    pub fn set_refresh(&mut self, refresh: u32) -> anyhow::Result<()> {\n        let mut inner = self.inner.borrow_mut();\n\n        if inner.refresh == refresh {\n            return Ok(());\n        }\n\n        let _span = tracy_client::span!(\"Cast::set_refresh\");\n        debug!(\"cast FPS changed, updating stream FPS\");\n        inner.refresh = refresh;\n\n        let size = inner.state.expected_format_size();\n        let params;\n        make_params!(params, &self.formats, size, refresh, self.offer_alpha);\n        self.stream\n            .update_params(params)\n            .context(\"error updating stream params\")?;\n\n        Ok(())\n    }\n\n    fn compute_extra_delay(&self, target_frame_time: Duration) -> Duration {\n        let inner = self.inner.borrow();\n\n        let last = self.last_frame_time;\n        let min = inner.min_time_between_frames;\n\n        if last.is_zero() {\n            trace!(?target_frame_time, ?last, \"last is zero, recording\");\n            return Duration::ZERO;\n        }\n\n        if target_frame_time < last {\n            // Record frame with a warning; in case it was an overflow this will fix it.\n            warn!(\n                ?target_frame_time,\n                ?last,\n                \"target frame time is below last, did it overflow or did we mispredict?\"\n            );\n            return Duration::ZERO;\n        }\n\n        let diff = target_frame_time - last;\n        if diff < min {\n            let delay = min - diff;\n            trace!(\n                ?target_frame_time,\n                ?last,\n                \"frame is too soon: min={min:?}, delay={:?}\",\n                delay\n            );\n            return delay;\n        } else {\n            trace!(\"overshoot={:?}\", diff - min);\n        }\n\n        Duration::ZERO\n    }\n\n    fn schedule_redraw(&mut self, output: Output, target_time: Duration) {\n        if self.scheduled_redraw.is_some() {\n            return;\n        }\n\n        let now = get_monotonic_time();\n        let duration = target_time.saturating_sub(now);\n        let timer = Timer::from_duration(duration);\n        let token = self\n            .event_loop\n            .insert_source(timer, move |_, _, state| {\n                // Guard against output disconnecting before the timer has a chance to run.\n                if state.niri.output_state.contains_key(&output) {\n                    state.niri.queue_redraw(&output);\n                }\n\n                TimeoutAction::Drop\n            })\n            .unwrap();\n        self.scheduled_redraw = Some(token);\n    }\n\n    fn remove_scheduled_redraw(&mut self) {\n        if let Some(token) = self.scheduled_redraw.take() {\n            self.event_loop.remove(token);\n        }\n    }\n\n    /// Checks whether this frame should be skipped because it's too soon.\n    ///\n    /// If the frame should be skipped, schedules a redraw and returns `true`. Otherwise, removes a\n    /// scheduled redraw, if any, and returns `false`.\n    ///\n    /// When this method returns `false`, the calling code is assumed to follow up with\n    /// [`Cast::dequeue_buffer_and_render()`].\n    pub fn check_time_and_schedule(\n        &mut self,\n        output: &Output,\n        target_frame_time: Duration,\n    ) -> bool {\n        let delay = self.compute_extra_delay(target_frame_time);\n        if delay >= CAST_DELAY_ALLOWANCE {\n            trace!(\"delay >= allowance, scheduling redraw\");\n            self.schedule_redraw(output.clone(), target_frame_time + delay);\n            true\n        } else {\n            self.remove_scheduled_redraw();\n            false\n        }\n    }\n\n    fn dequeue_available_buffer(&mut self) -> Option<NonNull<pw_buffer>> {\n        unsafe { NonNull::new(self.stream.dequeue_raw_buffer()) }\n    }\n\n    fn queue_completed_buffers(&mut self) {\n        let mut inner = self.inner.borrow_mut();\n\n        // We want to queue buffers in order, so find the first still-rendering buffer, and queue\n        // everything up to that. Even if there are completed buffers past the first\n        // still-rendering buffer, we do not want to queue them, since that would send frames out\n        // of order.\n        let first_in_progress_idx = inner\n            .rendering_buffers\n            .iter()\n            .position(|(_, sync)| !sync.is_reached())\n            .unwrap_or(inner.rendering_buffers.len());\n\n        for (buffer, _) in inner.rendering_buffers.drain(..first_in_progress_idx) {\n            trace!(\"queueing completed buffer\");\n            unsafe {\n                pw_stream_queue_buffer(self.stream.as_raw_ptr(), buffer.as_ptr());\n            }\n        }\n    }\n\n    unsafe fn queue_after_sync(&mut self, pw_buffer: NonNull<pw_buffer>, sync_point: SyncPoint) {\n        let _span = tracy_client::span!(\"Cast::queue_after_sync\");\n\n        let mut inner = self.inner.borrow_mut();\n\n        let mut sync_point = sync_point;\n        let sync_fd = match sync_point.export() {\n            Some(sync_fd) => Some(sync_fd),\n            None => {\n                // There are two main ways this can happen. First is that the SyncPoint is\n                // pre-signalled, then the buffer is already ready and no waiting is needed. Second\n                // is that the SyncPoint is potentially still not signalled, but exporting a fence\n                // fd had failed. In this case, there's not much we can do (perhaps do a blocking\n                // wait for the SyncPoint, which itself might fail).\n                //\n                // So let's hope for the best and mark the buffer as submittable. We do not reuse\n                // the original SyncPoint because if we do hit the second case (when it's not\n                // signalled), then without a sync fd we cannot schedule a queue upon its\n                // completion, effectively going stuck. It's better to queue an incomplete buffer\n                // than getting stuck.\n                sync_point = SyncPoint::signaled();\n                None\n            }\n        };\n\n        inner.rendering_buffers.push((pw_buffer, sync_point));\n        drop(inner);\n\n        match sync_fd {\n            None => {\n                trace!(\"sync_fd is None, queueing completed buffers\");\n                // In case this is the only buffer in the list, we will queue it right away.\n                self.queue_completed_buffers();\n            }\n            Some(sync_fd) => {\n                trace!(\"scheduling buffer to queue\");\n                let stream_id = self.stream_id;\n                let source = Generic::new(sync_fd, Interest::READ, Mode::OneShot);\n                self.event_loop\n                    .insert_source(source, move |_, _, state| {\n                        for cast in &mut state.niri.casting.casts {\n                            if cast.stream_id == stream_id {\n                                cast.queue_completed_buffers();\n                            }\n                        }\n\n                        Ok(PostAction::Remove)\n                    })\n                    .unwrap();\n            }\n        }\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn dequeue_buffer_and_render(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        elements: &[CastRenderElement<GlesRenderer>],\n        cursor_data: &CursorData<CastRenderElement<GlesRenderer>>,\n        size: Size<i32, Physical>,\n        scale: Scale<f64>,\n    ) -> bool {\n        let mut inner = self.inner.borrow_mut();\n\n        let CastState::Ready {\n            damage_tracker,\n            cursor_damage_tracker,\n            last_cursor_location,\n            ..\n        } = &mut inner.state\n        else {\n            error!(\"cast must be in Ready state to render\");\n            return false;\n        };\n        let damage_tracker = damage_tracker\n            .get_or_insert_with(|| OutputDamageTracker::new(size, scale, Transform::Normal));\n        let cursor_damage_tracker = cursor_damage_tracker.get_or_insert_with(|| {\n            OutputDamageTracker::new(\n                Size::from((CURSOR_WIDTH as _, CURSOR_HEIGHT as _)),\n                scale,\n                Transform::Normal,\n            )\n        });\n\n        // Size change will drop the damage tracker, but scale change won't, so check it here.\n        let OutputModeSource::Static { scale: t_scale, .. } = damage_tracker.mode() else {\n            unreachable!();\n        };\n        if *t_scale != scale {\n            *damage_tracker = OutputDamageTracker::new(size, scale, Transform::Normal);\n            *cursor_damage_tracker = OutputDamageTracker::new(\n                Size::from((CURSOR_WIDTH as _, CURSOR_HEIGHT as _)),\n                scale,\n                Transform::Normal,\n            );\n        }\n\n        let (damage, _states) = damage_tracker.damage_output(1, elements).unwrap();\n\n        let mut has_cursor_update = false;\n        let mut redraw_cursor = false;\n        if self.cursor_mode != CursorMode::Hidden {\n            let (damage, _states) = cursor_damage_tracker\n                .damage_output(1, &cursor_data.relocated)\n                .unwrap();\n            redraw_cursor = damage.is_some();\n            has_cursor_update =\n                redraw_cursor || *last_cursor_location != Some(cursor_data.location);\n        }\n\n        if damage.is_none() && !has_cursor_update {\n            trace!(\"no damage, skipping frame\");\n            return false;\n        }\n        *last_cursor_location = Some(cursor_data.location);\n        drop(inner);\n\n        let Some(pw_buffer) = self.dequeue_available_buffer() else {\n            warn!(\"no available buffer in pw stream, skipping frame\");\n            return false;\n        };\n        let buffer = pw_buffer.as_ptr();\n\n        unsafe {\n            let spa_buffer = (*buffer).buffer;\n\n            let mut pointer_elements = None;\n            if self.cursor_mode == CursorMode::Metadata {\n                add_cursor_metadata(renderer, spa_buffer, cursor_data, redraw_cursor);\n            } else if self.cursor_mode != CursorMode::Hidden {\n                // Embed the cursor into the main render.\n                pointer_elements = Some(cursor_data.original.iter());\n            }\n            let pointer_elements = pointer_elements.into_iter().flatten();\n            let elements = pointer_elements.chain(elements);\n\n            // FIXME: would be good to skip rendering the full frame if only the pointer changed.\n            // Unfortunately, I think the OBS PipeWire code needs to be updated first to cleanly\n            // allow for that codepath.\n            let fd = (*(*spa_buffer).datas).fd;\n            let dmabuf = self.inner.borrow().dmabufs[&fd].clone();\n\n            match render_to_dmabuf(\n                renderer,\n                dmabuf,\n                size,\n                scale,\n                Transform::Normal,\n                elements.rev(),\n            ) {\n                Ok(sync_point) => {\n                    mark_buffer_as_good(pw_buffer, &mut self.sequence_counter);\n                    trace!(\"queueing buffer with seq={}\", self.sequence_counter);\n                    self.queue_after_sync(pw_buffer, sync_point);\n                    true\n                }\n                Err(err) => {\n                    warn!(\"error rendering to dmabuf: {err:?}\");\n                    return_unused_buffer(&self.stream, pw_buffer);\n                    false\n                }\n            }\n        }\n    }\n\n    pub fn dequeue_buffer_and_clear(&mut self, renderer: &mut GlesRenderer) -> bool {\n        let mut inner = self.inner.borrow_mut();\n\n        // Clear out the damage tracker if we're in Ready state.\n        if let CastState::Ready {\n            damage_tracker,\n            cursor_damage_tracker,\n            ..\n        } = &mut inner.state\n        {\n            *damage_tracker = None;\n            *cursor_damage_tracker = None;\n        };\n        drop(inner);\n\n        let Some(pw_buffer) = self.dequeue_available_buffer() else {\n            warn!(\"no available buffer in pw stream, skipping frame\");\n            return false;\n        };\n        let buffer = pw_buffer.as_ptr();\n\n        unsafe {\n            let spa_buffer = (*buffer).buffer;\n\n            if self.cursor_mode == CursorMode::Metadata {\n                add_invisible_cursor(spa_buffer);\n            }\n\n            let fd = (*(*spa_buffer).datas).fd;\n            let dmabuf = self.inner.borrow().dmabufs[&fd].clone();\n\n            match clear_dmabuf(renderer, dmabuf) {\n                Ok(sync_point) => {\n                    mark_buffer_as_good(pw_buffer, &mut self.sequence_counter);\n                    trace!(\"queueing clear buffer with seq={}\", self.sequence_counter);\n                    self.queue_after_sync(pw_buffer, sync_point);\n                    true\n                }\n                Err(err) => {\n                    warn!(\"error clearing dmabuf: {err:?}\");\n                    return_unused_buffer(&self.stream, pw_buffer);\n                    false\n                }\n            }\n        }\n    }\n}\n\nimpl CastState {\n    fn pending_size(&self) -> Option<Size<u32, Physical>> {\n        match self {\n            CastState::ResizePending { pending_size } => Some(*pending_size),\n            CastState::ConfirmationPending { size, .. } => Some(*size),\n            CastState::Ready { .. } => None,\n        }\n    }\n\n    fn expected_format_size(&self) -> Size<u32, Physical> {\n        match self {\n            CastState::ResizePending { pending_size } => *pending_size,\n            CastState::ConfirmationPending { size, .. } => *size,\n            CastState::Ready { size, .. } => *size,\n        }\n    }\n}\n\nfn pw_version_supports_cursor_metadata() -> bool {\n    // This PipeWire version fixed a critical memory issue with cursor metadata:\n    // https://gitlab.freedesktop.org/pipewire/pipewire/-/merge_requests/2538\n    unsafe { pw_check_library_version(1, 4, 8) }\n}\n\nfn make_video_params(\n    formats: &FormatSet,\n    size: Size<u32, Physical>,\n    refresh: u32,\n    alpha: bool,\n) -> pod::Object {\n    let format = if alpha {\n        VideoFormat::BGRA\n    } else {\n        VideoFormat::BGRx\n    };\n\n    let fourcc = if alpha {\n        Fourcc::Argb8888\n    } else {\n        Fourcc::Xrgb8888\n    };\n\n    let formats: Vec<_> = formats\n        .iter()\n        .filter_map(|f| (f.code == fourcc).then_some(u64::from(f.modifier) as i64))\n        .collect();\n\n    trace!(\"offering: {formats:?}\");\n\n    let dont_fixate = if formats.len() > 1 {\n        PropertyFlags::DONT_FIXATE\n    } else {\n        PropertyFlags::empty()\n    };\n\n    pod::object!(\n        SpaTypes::ObjectParamFormat,\n        ParamType::EnumFormat,\n        pod::property!(FormatProperties::MediaType, Id, MediaType::Video),\n        pod::property!(FormatProperties::MediaSubtype, Id, MediaSubtype::Raw),\n        pod::property!(FormatProperties::VideoFormat, Id, format),\n        Property {\n            key: FormatProperties::VideoModifier.as_raw(),\n            flags: PropertyFlags::MANDATORY | dont_fixate,\n            value: pod::Value::Choice(ChoiceValue::Long(Choice(\n                ChoiceFlags::empty(),\n                ChoiceEnum::Enum {\n                    default: formats[0],\n                    alternatives: formats,\n                }\n            )))\n        },\n        pod::property!(\n            FormatProperties::VideoSize,\n            Rectangle,\n            Rectangle {\n                width: size.w,\n                height: size.h,\n            }\n        ),\n        pod::property!(\n            FormatProperties::VideoFramerate,\n            Fraction,\n            Fraction { num: 0, denom: 1 }\n        ),\n        pod::property!(\n            FormatProperties::VideoMaxFramerate,\n            Choice,\n            Range,\n            Fraction,\n            Fraction {\n                num: refresh,\n                denom: 1000\n            },\n            Fraction { num: 1, denom: 1 },\n            Fraction {\n                num: refresh,\n                denom: 1000\n            }\n        ),\n    )\n}\n\nfn make_pod(buffer: &mut Vec<u8>, object: pod::Object) -> &Pod {\n    PodSerializer::serialize(Cursor::new(&mut *buffer), &pod::Value::Object(object)).unwrap();\n    Pod::from_bytes(buffer).unwrap()\n}\n\nfn find_preferred_modifier(\n    gbm: &GbmDevice<DrmDeviceFd>,\n    size: Size<u32, Physical>,\n    fourcc: Fourcc,\n    modifiers: Vec<i64>,\n) -> anyhow::Result<(Modifier, usize)> {\n    debug!(\"find_preferred_modifier: size={size:?}, fourcc={fourcc}, modifiers={modifiers:?}\");\n\n    let (buffer, modifier) = allocate_buffer(gbm, size, fourcc, &modifiers)?;\n\n    let dmabuf = buffer\n        .export()\n        .context(\"error exporting GBM buffer object as dmabuf\")?;\n    let plane_count = dmabuf.num_planes();\n\n    // FIXME: Ideally this also needs to try binding the dmabuf for rendering.\n\n    Ok((modifier, plane_count))\n}\n\nfn allocate_buffer(\n    gbm: &GbmDevice<DrmDeviceFd>,\n    size: Size<u32, Physical>,\n    fourcc: Fourcc,\n    modifiers: &[i64],\n) -> anyhow::Result<(GbmBuffer, Modifier)> {\n    let (w, h) = (size.w, size.h);\n    let flags = GbmBufferFlags::RENDERING;\n\n    if modifiers.len() == 1 && Modifier::from(modifiers[0] as u64) == Modifier::Invalid {\n        let bo = gbm\n            .create_buffer_object::<()>(w, h, fourcc, flags)\n            .context(\"error creating GBM buffer object\")?;\n\n        let buffer = GbmBuffer::from_bo(bo, true);\n        Ok((buffer, Modifier::Invalid))\n    } else {\n        let modifiers = modifiers\n            .iter()\n            .map(|m| Modifier::from(*m as u64))\n            .filter(|m| *m != Modifier::Invalid);\n\n        let bo = gbm\n            .create_buffer_object_with_modifiers2::<()>(w, h, fourcc, modifiers, flags)\n            .context(\"error creating GBM buffer object\")?;\n\n        let modifier = bo.modifier();\n        let buffer = GbmBuffer::from_bo(bo, false);\n        Ok((buffer, modifier))\n    }\n}\n\nfn allocate_dmabuf(\n    gbm: &GbmDevice<DrmDeviceFd>,\n    size: Size<u32, Physical>,\n    fourcc: Fourcc,\n    modifier: Modifier,\n) -> anyhow::Result<Dmabuf> {\n    let (buffer, _modifier) = allocate_buffer(gbm, size, fourcc, &[u64::from(modifier) as i64])?;\n    let dmabuf = buffer\n        .export()\n        .context(\"error exporting GBM buffer object as dmabuf\")?;\n    Ok(dmabuf)\n}\n\nunsafe fn return_unused_buffer(stream: &Stream, pw_buffer: NonNull<pw_buffer>) {\n    // pw_stream_return_buffer() requires too new PipeWire (1.4.0). So, mark as\n    // corrupted and queue.\n    let pw_buffer = pw_buffer.as_ptr();\n    let spa_buffer = (*pw_buffer).buffer;\n    let chunk = (*(*spa_buffer).datas).chunk;\n    // Some (older?) consumers will check for size == 0 instead of the CORRUPTED flag.\n    (*chunk).size = 0;\n    (*chunk).flags = SPA_CHUNK_FLAG_CORRUPTED as i32;\n\n    if let Some(header) = find_meta_header(spa_buffer) {\n        let header = header.as_ptr();\n        (*header).flags = SPA_META_HEADER_FLAG_CORRUPTED;\n    }\n\n    pw_stream_queue_buffer(stream.as_raw_ptr(), pw_buffer);\n}\n\nunsafe fn mark_buffer_as_good(pw_buffer: NonNull<pw_buffer>, sequence: &mut u64) {\n    let pw_buffer = pw_buffer.as_ptr();\n    let spa_buffer = (*pw_buffer).buffer;\n    let chunk = (*(*spa_buffer).datas).chunk;\n\n    // With DMA-BUFs, consumers should ignore the size field, and producers are allowed\n    // to set it to 0.\n    //\n    // https://docs.pipewire.org/page_dma_buf.html\n    //\n    // However, OBS checks for size != 0 as a workaround for old compositor versions,\n    // so we set it to 1.\n    (*chunk).size = 1;\n    // Clear the corrupted flag we may have set before.\n    (*chunk).flags = SPA_CHUNK_FLAG_NONE as i32;\n\n    *sequence = sequence.wrapping_add(1);\n    if let Some(header) = find_meta_header(spa_buffer) {\n        let header = header.as_ptr();\n        // Clear the corrupted flag we may have set before.\n        (*header).flags = 0;\n        (*header).seq = *sequence;\n    }\n}\n\nunsafe fn find_meta_header(buffer: *mut spa_buffer) -> Option<NonNull<spa_meta_header>> {\n    let p = spa_buffer_find_meta_data(buffer, SPA_META_Header, size_of::<spa_meta_header>()).cast();\n    NonNull::new(p)\n}\n\nunsafe fn add_invisible_cursor(spa_buffer: *mut spa_buffer) {\n    unsafe {\n        let cursor_meta_ptr: *mut spa_meta_cursor = spa_buffer_find_meta_data(\n            spa_buffer,\n            SPA_META_Cursor,\n            mem::size_of::<spa_meta_cursor>(),\n        )\n        .cast();\n        let Some(cursor_meta) = cursor_meta_ptr.as_mut() else {\n            return;\n        };\n\n        // The cursor is present but invisible.\n        cursor_meta.id = 1;\n        cursor_meta.position.x = 0;\n        cursor_meta.position.y = 0;\n        cursor_meta.hotspot.x = 0;\n        cursor_meta.hotspot.y = 0;\n        cursor_meta.bitmap_offset = BITMAP_META_OFFSET as _;\n\n        let bitmap_meta_ptr = cursor_meta_ptr\n            .byte_add(BITMAP_META_OFFSET)\n            .cast::<spa_meta_bitmap>();\n        let bitmap_meta = &mut *bitmap_meta_ptr;\n\n        // HACK: PipeWire docs say offset = 0 means invisible.\n        //\n        // Unfortunately, OBS doesn't actually check that, instead it checks that size isn't zero:\n        // https://github.com/obsproject/obs-studio/blob/f4aaa5f0417c5ec40a3799551e125129fce1e007/plugins/linux-pipewire/pipewire.c#L900\n        //\n        // Unfortunately, libwebrtc, on top of ignoring offset, also treats size = 0 as \"preserve\n        // previous cursor\":\n        // https://webrtc.googlesource.com/src/+/97b46e12582606a238d4f0c8524365cf5bdcb411/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc#765\n        //\n        // So, send a 1x1 transparent pixel instead...\n        bitmap_meta.offset = BITMAP_DATA_OFFSET as _;\n        bitmap_meta.size.width = 1;\n        bitmap_meta.size.height = 1;\n        bitmap_meta.stride = CURSOR_BPP as i32;\n        bitmap_meta.format = CURSOR_FORMAT;\n\n        let bitmap_data = bitmap_meta_ptr.cast::<u8>().add(BITMAP_DATA_OFFSET);\n        let bitmap_slice = slice::from_raw_parts_mut(bitmap_data, CURSOR_BITMAP_SIZE);\n        bitmap_slice[..4].copy_from_slice(&[0, 0, 0, 0]);\n    }\n}\n\nunsafe fn add_cursor_metadata(\n    renderer: &mut GlesRenderer,\n    spa_buffer: *mut spa_buffer,\n    cursor_data: &CursorData<impl RenderElement<GlesRenderer>>,\n    redraw: bool,\n) {\n    unsafe {\n        let cursor_meta_ptr: *mut spa_meta_cursor = spa_buffer_find_meta_data(\n            spa_buffer,\n            SPA_META_Cursor,\n            mem::size_of::<spa_meta_cursor>(),\n        )\n        .cast();\n        let Some(cursor_meta) = cursor_meta_ptr.as_mut() else {\n            return;\n        };\n\n        cursor_meta.id = 1;\n        cursor_meta.position.x = cursor_data.location.x;\n        cursor_meta.position.y = cursor_data.location.y;\n        cursor_meta.hotspot.x = cursor_data.hotspot.x;\n        cursor_meta.hotspot.y = cursor_data.hotspot.y;\n\n        if !redraw {\n            trace!(\"cursor not damaged, skipping rerendering\");\n            cursor_meta.bitmap_offset = 0;\n            return;\n        }\n\n        cursor_meta.bitmap_offset = BITMAP_META_OFFSET as _;\n\n        let bitmap_meta_ptr = cursor_meta_ptr\n            .byte_add(BITMAP_META_OFFSET)\n            .cast::<spa_meta_bitmap>();\n        let bitmap_meta = &mut *bitmap_meta_ptr;\n\n        // Start with a 1x1 transparent pixel; see comment in add_invisible_cursor().\n        bitmap_meta.offset = BITMAP_DATA_OFFSET as _;\n        bitmap_meta.size.width = 1;\n        bitmap_meta.size.height = 1;\n        bitmap_meta.stride = CURSOR_BPP as i32;\n        bitmap_meta.format = CURSOR_FORMAT;\n\n        let bitmap_data = bitmap_meta_ptr.cast::<u8>().add(BITMAP_DATA_OFFSET);\n        let bitmap_slice = slice::from_raw_parts_mut(bitmap_data, CURSOR_BITMAP_SIZE);\n        bitmap_slice[..4].copy_from_slice(&[0, 0, 0, 0]);\n\n        let size = Size::new(\n            min(cursor_data.size.w, CURSOR_WIDTH as i32),\n            min(cursor_data.size.h, CURSOR_HEIGHT as i32),\n        );\n        if size.w == 0 || size.h == 0 {\n            trace!(\"cursor is invisible, skipping rendering\");\n            return;\n        }\n\n        let _span = tracy_client::span!(\"add_cursor_metadata render cursor\");\n\n        // FIXME: use a reliable buffer whenever we're rendering the cursor.\n        //\n        // PipeWire buffers are not normally guaranteed to reach the destination, so our buffer\n        // with the rendered cursor bitmap may not reach the consumer.\n        //\n        // Reliable buffers should be available starting from 1.6.0:\n        // https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/4885\n        let mapping = match render_and_download(\n            renderer,\n            size,\n            cursor_data.scale,\n            Transform::Normal,\n            Fourcc::Argb8888,\n            cursor_data.relocated.iter().rev(),\n        ) {\n            Ok(mapping) => mapping,\n            Err(err) => {\n                warn!(\"error rendering cursor: {err:?}\");\n                return;\n            }\n        };\n        let pixels = match renderer.map_texture(&mapping) {\n            Ok(pixels) => pixels,\n            Err(err) => {\n                warn!(\"error mapping cursor texture: {err:?}\");\n                return;\n            }\n        };\n\n        bitmap_slice[..pixels.len()].copy_from_slice(pixels);\n\n        // Fill the metadata now that everything succeeded.\n        bitmap_meta.size.width = size.w as _;\n        bitmap_meta.size.height = size.h as _;\n        bitmap_meta.stride = size.w * CURSOR_BPP as i32;\n    }\n}\n"
  },
  {
    "path": "src/tests/animations.rs",
    "content": "use std::fmt::Write as _;\nuse std::time::Duration;\n\nuse insta::assert_snapshot;\nuse niri_config::animations::{Curve, EasingParams, Kind};\nuse niri_config::Config;\nuse niri_ipc::SizeChange;\nuse smithay::utils::{Point, Size};\nuse wayland_client::protocol::wl_surface::WlSurface;\n\nuse super::client::ClientId;\nuse super::*;\nuse crate::niri::Niri;\n\nfn format_tiles(niri: &Niri) -> String {\n    let mut buf = String::new();\n    let ws = niri.layout.active_workspace().unwrap();\n    let mut tiles: Vec<_> = ws.tiles_with_render_positions().collect();\n\n    // We sort by id since that gives us a consistent order (from first opened to last), but we\n    // don't print the id since it's nondeterministic (the id is a global counter across all\n    // running tests in the same binary).\n    tiles.sort_by_key(|(tile, _, _)| tile.window().id().get());\n    for (tile, pos, _visible) in tiles {\n        let Size { w, h, .. } = tile.animated_tile_size();\n        let Point { x, y, .. } = pos;\n        writeln!(&mut buf, \"{w:>3.0} × {h:>3.0} at x:{x:>3.0} y:{y:>3.0}\").unwrap();\n    }\n    buf\n}\n\nfn create_window(f: &mut Fixture, id: ClientId, w: u16, h: u16) -> WlSurface {\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    window.attach_new_buffer();\n    window.set_size(w, h);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    surface\n}\n\nfn set_time(niri: &mut Niri, time: Duration) {\n    // This is a bit involved because we're dealing with an AdjustableClock that maintains its own\n    // internal current_time.\n\n    // First, reset current_time to zero by matching unadjusted time to it (at rate 0.0), then\n    // setting unadjusted time to zero at rate 1.0 (causing current_time to also go to zero).\n    let now = niri.clock.now();\n    niri.clock.set_unadjusted(now);\n    let _ = niri.clock.now();\n    niri.clock.set_unadjusted(Duration::ZERO);\n    niri.clock.set_rate(1.0);\n    let _ = niri.clock.now();\n\n    // Now, set the desired time at rate 1.0.\n    niri.clock.set_unadjusted(time);\n    let _ = niri.clock.now();\n\n    // Freeze the clock so that clear() inside the niri loop callback followed by some get()\n    // doesn't replace it with the monotonic time.\n    niri.clock.set_rate(0.0);\n}\n\n// Sets up a fixture with linear animations, a renderer, and an output.\nfn set_up() -> Fixture {\n    const LINEAR: Kind = Kind::Easing(EasingParams {\n        duration_ms: 1000,\n        curve: Curve::Linear,\n    });\n\n    let mut config = Config::default();\n    config.layout.gaps = 0.0;\n    config.animations.window_resize.anim.kind = LINEAR;\n    config.animations.window_movement.0.kind = LINEAR;\n\n    let mut f = Fixture::with_config(config);\n    f.niri_state().backend.headless().add_renderer().unwrap();\n    f.add_output(1, (1920, 1080));\n\n    f\n}\n\nfn set_up_two_in_column() -> (Fixture, ClientId, WlSurface, WlSurface) {\n    let mut f = set_up();\n\n    let id = f.add_client();\n\n    let surface1 = create_window(&mut f, id, 100, 100);\n    let surface2 = create_window(&mut f, id, 200, 200);\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface1).recent_configures();\n    let _ = f.client(id).window(&surface2).recent_configures();\n\n    // Consume into one column.\n    f.niri().layout.focus_left();\n    f.niri().layout.consume_into_column();\n    f.double_roundtrip(id);\n\n    // Commit for the column consume.\n    let window = f.client(id).window(&surface1);\n    window.ack_last_and_commit();\n\n    let window = f.client(id).window(&surface2);\n    window.ack_last_and_commit();\n\n    f.double_roundtrip(id);\n\n    set_time(f.niri(), Duration::ZERO);\n    f.niri_complete_animations();\n\n    (f, id, surface1, surface2)\n}\n\n#[test]\nfn egl_height_resize_animates_next_y() {\n    let (mut f, id, surface1, surface2) = set_up_two_in_column();\n\n    // Issue a resize.\n    f.niri()\n        .layout\n        .set_window_height(None, SizeChange::AdjustFixed(-50));\n    f.double_roundtrip(id);\n\n    // The top window shrinks in response, the bottom remains as is.\n    let window = f.client(id).window(&surface1);\n    window.set_size(100, 50);\n    window.ack_last_and_commit();\n    let window = f.client(id).window(&surface2);\n    window.ack_last_and_commit();\n\n    // This starts the resize animation for the top window and the Y move for the bottom.\n    f.roundtrip(id);\n\n    // No time had passed yet, so we're at the initial state.\n    assert_snapshot!(format_tiles(f.niri()), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n\n    // Advance the time halfway.\n    set_time(f.niri(), Duration::from_millis(500));\n    f.niri().advance_animations();\n\n    // Top window is half-resized at 75 px tall, bottom window is at y=75 matching it.\n    assert_snapshot!(format_tiles(f.niri()), @r\"\n    100 ×  75 at x:  0 y:  0\n    200 × 200 at x:  0 y: 75\n    \");\n\n    // Advance the time to completion.\n    set_time(f.niri(), Duration::from_millis(1000));\n    f.niri().advance_animations();\n\n    // Final state at 50 px.\n    assert_snapshot!(format_tiles(f.niri()), @r\"\n    100 ×  50 at x:  0 y:  0\n    200 × 200 at x:  0 y: 50\n    \");\n}\n\n#[test]\nfn egl_clientside_height_change_doesnt_animate() {\n    let (mut f, id, surface1, _surface2) = set_up_two_in_column();\n\n    // The initial state.\n    assert_snapshot!(format_tiles(f.niri()), @r\"\n    100 × 100 at x:  0 y:  0\n    200 × 200 at x:  0 y:100\n    \");\n\n    // The top window shrinks by itself, without a niri-issued resize.\n    let window = f.client(id).window(&surface1);\n    window.set_size(100, 50);\n    window.commit();\n\n    // This does not start any animations.\n    f.roundtrip(id);\n\n    // No time had passed yet, but we are at the final state right away.\n    assert_snapshot!(format_tiles(f.niri()), @r\"\n    100 ×  50 at x:  0 y:  0\n    200 × 200 at x:  0 y: 50\n    \");\n}\n"
  },
  {
    "path": "src/tests/client.rs",
    "content": "use std::cmp::min;\nuse std::collections::HashMap;\nuse std::fmt;\nuse std::fmt::Write as _;\nuse std::os::unix::net::UnixStream;\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse calloop::EventLoop;\nuse calloop_wayland_source::WaylandSource;\nuse single_pixel_buffer::v1::client::wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1;\nuse smithay::reexports::wayland_protocols::wp::single_pixel_buffer;\nuse smithay::reexports::wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;\nuse smithay::reexports::wayland_protocols::wp::viewporter::client::wp_viewporter::WpViewporter;\nuse smithay::reexports::wayland_protocols::xdg::shell::client::xdg_surface::{self, XdgSurface};\nuse smithay::reexports::wayland_protocols::xdg::shell::client::xdg_toplevel::{self, XdgToplevel};\nuse smithay::reexports::wayland_protocols::xdg::shell::client::xdg_wm_base::{self, XdgWmBase};\nuse smithay::reexports::wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::{\n    self, ZwlrLayerShellV1,\n};\nuse smithay::reexports::wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::{\n    self, ZwlrLayerSurfaceV1,\n};\nuse wayland_backend::client::Backend;\nuse wayland_client::globals::Global;\nuse wayland_client::protocol::wl_buffer::{self, WlBuffer};\nuse wayland_client::protocol::wl_callback::{self, WlCallback};\nuse wayland_client::protocol::wl_compositor::WlCompositor;\nuse wayland_client::protocol::wl_display::WlDisplay;\nuse wayland_client::protocol::wl_output::{self, WlOutput};\nuse wayland_client::protocol::wl_registry::{self, WlRegistry};\nuse wayland_client::protocol::wl_surface::{self, WlSurface};\nuse wayland_client::{Connection, Dispatch, Proxy as _, QueueHandle};\n\nuse crate::utils::id::IdCounter;\n\npub struct Client {\n    pub id: ClientId,\n    pub event_loop: EventLoop<'static, State>,\n    pub connection: Connection,\n    pub qh: QueueHandle<State>,\n    pub display: WlDisplay,\n    pub state: State,\n}\n\npub struct State {\n    pub qh: QueueHandle<State>,\n\n    pub globals: Vec<Global>,\n    pub outputs: HashMap<WlOutput, String>,\n\n    pub compositor: Option<WlCompositor>,\n    pub xdg_wm_base: Option<XdgWmBase>,\n    pub layer_shell: Option<ZwlrLayerShellV1>,\n    pub spbm: Option<WpSinglePixelBufferManagerV1>,\n    pub viewporter: Option<WpViewporter>,\n\n    pub windows: Vec<Window>,\n    pub layers: Vec<LayerSurface>,\n}\n\npub struct Window {\n    pub qh: QueueHandle<State>,\n    pub spbm: WpSinglePixelBufferManagerV1,\n\n    pub surface: WlSurface,\n    pub xdg_surface: XdgSurface,\n    pub xdg_toplevel: XdgToplevel,\n    pub viewport: WpViewport,\n    pub pending_configure: Configure,\n    pub configures_received: Vec<(u32, Configure)>,\n    pub close_requested: bool,\n\n    pub configures_looked_at: usize,\n}\n\npub struct LayerSurface {\n    pub qh: QueueHandle<State>,\n    pub spbm: WpSinglePixelBufferManagerV1,\n\n    pub surface: WlSurface,\n    pub layer_surface: ZwlrLayerSurfaceV1,\n    pub viewport: WpViewport,\n    pub configures_received: Vec<(u32, LayerConfigure)>,\n    pub close_requested: bool,\n\n    pub configures_looked_at: usize,\n}\n\n#[derive(Debug, Clone, Default)]\npub struct Configure {\n    pub size: (i32, i32),\n    pub bounds: Option<(i32, i32)>,\n    pub states: Vec<xdg_toplevel::State>,\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct LayerConfigure {\n    pub size: (u32, u32),\n}\n\n#[derive(Clone, Copy, Default)]\npub struct LayerMargin {\n    pub top: i32,\n    pub right: i32,\n    pub bottom: i32,\n    pub left: i32,\n}\n\n#[derive(Clone, Copy, Default)]\npub struct LayerConfigureProps {\n    pub size: Option<(u32, u32)>,\n    pub anchor: Option<zwlr_layer_surface_v1::Anchor>,\n    pub exclusive_zone: Option<i32>,\n    pub margin: Option<LayerMargin>,\n    pub kb_interactivity: Option<zwlr_layer_surface_v1::KeyboardInteractivity>,\n    pub layer: Option<zwlr_layer_shell_v1::Layer>,\n    pub exclusive_edge: Option<zwlr_layer_surface_v1::Anchor>,\n}\n\n#[derive(Default)]\npub struct SyncData {\n    pub done: AtomicBool,\n}\n\nstatic CLIENT_ID_COUNTER: IdCounter = IdCounter::new();\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct ClientId(u64);\n\nimpl ClientId {\n    fn next() -> ClientId {\n        ClientId(CLIENT_ID_COUNTER.next())\n    }\n}\n\nimpl fmt::Display for Configure {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"size: {} × {}, \", self.size.0, self.size.1)?;\n        if let Some(bounds) = self.bounds {\n            write!(f, \"bounds: {} × {}, \", bounds.0, bounds.1)?;\n        } else {\n            write!(f, \"bounds: none, \")?;\n        }\n        write!(f, \"states: {:?}\", self.states)?;\n        Ok(())\n    }\n}\n\nimpl fmt::Display for LayerConfigure {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"size: {} × {}\", self.size.0, self.size.1)?;\n        Ok(())\n    }\n}\n\nimpl Client {\n    pub fn new(stream: UnixStream) -> Self {\n        let id = ClientId::next();\n\n        let event_loop = EventLoop::try_new().unwrap();\n        let backend = Backend::connect(stream).unwrap();\n        let connection = Connection::from_backend(backend);\n        let queue = connection.new_event_queue();\n        let qh = queue.handle();\n        WaylandSource::new(connection.clone(), queue)\n            .insert(event_loop.handle())\n            .unwrap();\n\n        let display = connection.display();\n        let _registry = display.get_registry(&qh, ());\n        connection.flush().unwrap();\n\n        let state = State {\n            qh: qh.clone(),\n            globals: Vec::new(),\n            outputs: HashMap::new(),\n            compositor: None,\n            xdg_wm_base: None,\n            layer_shell: None,\n            spbm: None,\n            viewporter: None,\n            windows: Vec::new(),\n            layers: Vec::new(),\n        };\n\n        Self {\n            id,\n            event_loop,\n            connection,\n            qh,\n            display,\n            state,\n        }\n    }\n\n    pub fn dispatch(&mut self) {\n        self.event_loop\n            .dispatch(Duration::ZERO, &mut self.state)\n            .unwrap();\n\n        if let Some(error) = self.connection.protocol_error() {\n            panic!(\"{error}\");\n        }\n    }\n\n    pub fn send_sync(&self) -> Arc<SyncData> {\n        let data = Arc::new(SyncData::default());\n        self.display.sync(&self.qh, data.clone());\n        self.connection.flush().unwrap();\n        data\n    }\n\n    pub fn create_window(&mut self) -> &mut Window {\n        self.state.create_window()\n    }\n\n    pub fn window(&mut self, surface: &WlSurface) -> &mut Window {\n        self.state.window(surface)\n    }\n\n    pub fn create_layer(\n        &mut self,\n        output: Option<&WlOutput>,\n        layer: zwlr_layer_shell_v1::Layer,\n        namespace: &str,\n    ) -> &mut LayerSurface {\n        self.state.create_layer(output, layer, namespace.to_owned())\n    }\n\n    pub fn layer(&mut self, surface: &WlSurface) -> &mut LayerSurface {\n        self.state.layer(surface)\n    }\n\n    pub fn output(&mut self, name: &str) -> WlOutput {\n        self.state\n            .outputs\n            .iter()\n            .find(|(_, v)| *v == name)\n            .unwrap()\n            .0\n            .clone()\n    }\n}\n\nimpl State {\n    pub fn create_window(&mut self) -> &mut Window {\n        let compositor = self.compositor.as_ref().unwrap();\n        let xdg_wm_base = self.xdg_wm_base.as_ref().unwrap();\n        let viewporter = self.viewporter.as_ref().unwrap();\n\n        let surface = compositor.create_surface(&self.qh, ());\n        let xdg_surface = xdg_wm_base.get_xdg_surface(&surface, &self.qh, ());\n        let xdg_toplevel = xdg_surface.get_toplevel(&self.qh, ());\n        let viewport = viewporter.get_viewport(&surface, &self.qh, ());\n\n        let window = Window {\n            qh: self.qh.clone(),\n            spbm: self.spbm.clone().unwrap(),\n\n            surface,\n            xdg_surface,\n            xdg_toplevel,\n            viewport,\n            pending_configure: Configure::default(),\n            configures_received: Vec::new(),\n            close_requested: false,\n\n            configures_looked_at: 0,\n        };\n\n        self.windows.push(window);\n        self.windows.last_mut().unwrap()\n    }\n\n    pub fn window(&mut self, surface: &WlSurface) -> &mut Window {\n        self.windows\n            .iter_mut()\n            .find(|w| w.surface == *surface)\n            .unwrap()\n    }\n\n    pub fn create_layer(\n        &mut self,\n        output: Option<&WlOutput>,\n        layer: zwlr_layer_shell_v1::Layer,\n        namespace: String,\n    ) -> &mut LayerSurface {\n        let compositor = self.compositor.as_ref().unwrap();\n        let layer_shell = self.layer_shell.as_ref().unwrap();\n        let viewporter = self.viewporter.as_ref().unwrap();\n\n        let surface = compositor.create_surface(&self.qh, ());\n        let layer_surface =\n            layer_shell.get_layer_surface(&surface, output, layer, namespace, &self.qh, ());\n        let viewport = viewporter.get_viewport(&surface, &self.qh, ());\n\n        let layer_surface = LayerSurface {\n            qh: self.qh.clone(),\n            spbm: self.spbm.clone().unwrap(),\n\n            surface,\n            layer_surface,\n            viewport,\n            configures_received: Vec::new(),\n            close_requested: false,\n\n            configures_looked_at: 0,\n        };\n\n        self.layers.push(layer_surface);\n        self.layers.last_mut().unwrap()\n    }\n\n    pub fn layer(&mut self, surface: &WlSurface) -> &mut LayerSurface {\n        self.layers\n            .iter_mut()\n            .find(|w| w.surface == *surface)\n            .unwrap()\n    }\n}\n\nimpl Window {\n    pub fn commit(&self) {\n        self.surface.commit();\n    }\n\n    pub fn ack_last(&self) {\n        let serial = self.configures_received.last().unwrap().0;\n        self.xdg_surface.ack_configure(serial);\n    }\n\n    pub fn ack_last_and_commit(&self) {\n        self.ack_last();\n        self.commit();\n    }\n\n    pub fn attach_new_buffer(&self) {\n        let buffer = self.spbm.create_u32_rgba_buffer(0, 0, 0, 0, &self.qh, ());\n        self.surface.attach(Some(&buffer), 0, 0);\n    }\n\n    pub fn attach_null(&self) {\n        self.surface.attach(None, 0, 0);\n    }\n\n    pub fn set_size(&self, w: u16, h: u16) {\n        self.viewport.set_destination(i32::from(w), i32::from(h));\n    }\n\n    pub fn set_fullscreen(&self, output: Option<&WlOutput>) {\n        self.xdg_toplevel.set_fullscreen(output);\n    }\n\n    pub fn unset_fullscreen(&self) {\n        self.xdg_toplevel.unset_fullscreen();\n    }\n\n    pub fn set_maximized(&self) {\n        self.xdg_toplevel.set_maximized();\n    }\n\n    pub fn unset_maximized(&self) {\n        self.xdg_toplevel.unset_maximized();\n    }\n\n    pub fn set_parent(&self, parent: Option<&XdgToplevel>) {\n        self.xdg_toplevel.set_parent(parent);\n    }\n\n    pub fn set_title(&self, title: &str) {\n        self.xdg_toplevel.set_title(title.to_owned());\n    }\n\n    pub fn recent_configures(&mut self) -> impl Iterator<Item = &Configure> {\n        let start = self.configures_looked_at;\n        self.configures_looked_at = self.configures_received.len();\n        self.configures_received[start..].iter().map(|(_, c)| c)\n    }\n\n    pub fn format_recent_configures(&mut self) -> String {\n        let mut buf = String::new();\n        for configure in self.recent_configures() {\n            if !buf.is_empty() {\n                buf.push('\\n');\n            }\n            write!(buf, \"{configure}\").unwrap();\n        }\n        buf\n    }\n}\n\nimpl LayerSurface {\n    pub fn commit(&self) {\n        self.surface.commit();\n    }\n\n    pub fn ack_last(&self) {\n        let serial = self.configures_received.last().unwrap().0;\n        self.layer_surface.ack_configure(serial);\n    }\n\n    pub fn ack_last_and_commit(&self) {\n        self.ack_last();\n        self.commit();\n    }\n\n    pub fn set_configure_props(&self, props: LayerConfigureProps) {\n        let LayerConfigureProps {\n            size,\n            anchor,\n            exclusive_zone,\n            margin,\n            kb_interactivity,\n            layer,\n            exclusive_edge,\n        } = props;\n\n        if let Some(x) = size {\n            self.layer_surface.set_size(x.0, x.1);\n        }\n        if let Some(x) = anchor {\n            self.layer_surface.set_anchor(x);\n        }\n        if let Some(x) = exclusive_zone {\n            self.layer_surface.set_exclusive_zone(x);\n        }\n        if let Some(x) = margin {\n            self.layer_surface\n                .set_margin(x.top, x.right, x.bottom, x.left);\n        }\n        if let Some(x) = kb_interactivity {\n            self.layer_surface.set_keyboard_interactivity(x);\n        }\n        if let Some(x) = layer {\n            self.layer_surface.set_layer(x);\n        }\n        if let Some(x) = exclusive_edge {\n            self.layer_surface.set_exclusive_edge(x);\n        }\n    }\n\n    pub fn attach_new_buffer(&self) {\n        let buffer = self.spbm.create_u32_rgba_buffer(0, 0, 0, 0, &self.qh, ());\n        self.surface.attach(Some(&buffer), 0, 0);\n    }\n\n    pub fn attach_null(&self) {\n        self.surface.attach(None, 0, 0);\n    }\n\n    pub fn set_size(&self, w: u16, h: u16) {\n        self.viewport.set_destination(i32::from(w), i32::from(h));\n    }\n\n    pub fn recent_configures(&mut self) -> impl Iterator<Item = &LayerConfigure> {\n        let start = self.configures_looked_at;\n        self.configures_looked_at = self.configures_received.len();\n        self.configures_received[start..].iter().map(|(_, c)| c)\n    }\n\n    pub fn format_recent_configures(&mut self) -> String {\n        let mut buf = String::new();\n        for configure in self.recent_configures() {\n            if !buf.is_empty() {\n                buf.push('\\n');\n            }\n            write!(buf, \"{configure}\").unwrap();\n        }\n        buf\n    }\n}\n\nimpl Dispatch<WlCallback, Arc<SyncData>> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &WlCallback,\n        event: <WlCallback as wayland_client::Proxy>::Event,\n        data: &Arc<SyncData>,\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        match event {\n            wl_callback::Event::Done { .. } => data.done.store(true, Ordering::Relaxed),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<WlRegistry, ()> for State {\n    fn event(\n        state: &mut Self,\n        registry: &WlRegistry,\n        event: <WlRegistry as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        qh: &QueueHandle<Self>,\n    ) {\n        match event {\n            wl_registry::Event::Global {\n                name,\n                interface,\n                version,\n            } => {\n                if interface == WlCompositor::interface().name {\n                    let version = min(version, WlCompositor::interface().version);\n                    state.compositor = Some(registry.bind(name, version, qh, ()));\n                } else if interface == XdgWmBase::interface().name {\n                    let version = min(version, XdgWmBase::interface().version);\n                    state.xdg_wm_base = Some(registry.bind(name, version, qh, ()));\n                } else if interface == ZwlrLayerShellV1::interface().name {\n                    let version = min(version, ZwlrLayerShellV1::interface().version);\n                    state.layer_shell = Some(registry.bind(name, version, qh, ()));\n                } else if interface == WpSinglePixelBufferManagerV1::interface().name {\n                    let version = min(version, WpSinglePixelBufferManagerV1::interface().version);\n                    state.spbm = Some(registry.bind(name, version, qh, ()));\n                } else if interface == WpViewporter::interface().name {\n                    let version = min(version, WpViewporter::interface().version);\n                    state.viewporter = Some(registry.bind(name, version, qh, ()));\n                } else if interface == WlOutput::interface().name {\n                    let version = min(version, WlOutput::interface().version);\n                    let output = registry.bind(name, version, qh, ());\n                    state.outputs.insert(output, String::new());\n                }\n\n                let global = Global {\n                    name,\n                    interface,\n                    version,\n                };\n                state.globals.push(global);\n            }\n            wl_registry::Event::GlobalRemove { .. } => (),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<WlOutput, ()> for State {\n    fn event(\n        state: &mut Self,\n        output: &WlOutput,\n        event: <WlOutput as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        match event {\n            wl_output::Event::Geometry { .. } => (),\n            wl_output::Event::Mode { .. } => (),\n            wl_output::Event::Done => (),\n            wl_output::Event::Scale { .. } => (),\n            wl_output::Event::Name { name } => {\n                *state.outputs.get_mut(output).unwrap() = name;\n            }\n            wl_output::Event::Description { .. } => (),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<WlCompositor, ()> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &WlCompositor,\n        _event: <WlCompositor as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        unreachable!()\n    }\n}\n\nimpl Dispatch<XdgWmBase, ()> for State {\n    fn event(\n        _state: &mut Self,\n        xdg_wm_base: &XdgWmBase,\n        event: <XdgWmBase as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        match event {\n            xdg_wm_base::Event::Ping { serial } => {\n                xdg_wm_base.pong(serial);\n            }\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<ZwlrLayerShellV1, ()> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &ZwlrLayerShellV1,\n        _event: <ZwlrLayerShellV1 as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        unreachable!()\n    }\n}\n\nimpl Dispatch<WlSurface, ()> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &WlSurface,\n        event: <WlSurface as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        match event {\n            wl_surface::Event::Enter { .. } => (),\n            wl_surface::Event::Leave { .. } => (),\n            wl_surface::Event::PreferredBufferScale { .. } => (),\n            wl_surface::Event::PreferredBufferTransform { .. } => (),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<XdgSurface, ()> for State {\n    fn event(\n        state: &mut Self,\n        xdg_surface: &XdgSurface,\n        event: <XdgSurface as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        match event {\n            xdg_surface::Event::Configure { serial } => {\n                let window = state\n                    .windows\n                    .iter_mut()\n                    .find(|w| w.xdg_surface == *xdg_surface)\n                    .unwrap();\n                let configure = window.pending_configure.clone();\n                window.configures_received.push((serial, configure));\n            }\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<XdgToplevel, ()> for State {\n    fn event(\n        state: &mut Self,\n        xdg_toplevel: &XdgToplevel,\n        event: <XdgToplevel as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        let window = state\n            .windows\n            .iter_mut()\n            .find(|w| w.xdg_toplevel == *xdg_toplevel)\n            .unwrap();\n\n        match event {\n            xdg_toplevel::Event::Configure {\n                width,\n                height,\n                states,\n            } => {\n                let configure = &mut window.pending_configure;\n                configure.size = (width, height);\n                configure.states = states\n                    .chunks_exact(4)\n                    .flat_map(TryInto::<[u8; 4]>::try_into)\n                    .map(u32::from_ne_bytes)\n                    .flat_map(xdg_toplevel::State::try_from)\n                    .collect();\n            }\n            xdg_toplevel::Event::Close => {\n                window.close_requested = true;\n            }\n            xdg_toplevel::Event::ConfigureBounds { width, height } => {\n                window.pending_configure.bounds = Some((width, height));\n            }\n            xdg_toplevel::Event::WmCapabilities { .. } => (),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<ZwlrLayerSurfaceV1, ()> for State {\n    fn event(\n        state: &mut Self,\n        layer_surface: &ZwlrLayerSurfaceV1,\n        event: <ZwlrLayerSurfaceV1 as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        let layer_surface = state\n            .layers\n            .iter_mut()\n            .find(|w| w.layer_surface == *layer_surface)\n            .unwrap();\n\n        match event {\n            zwlr_layer_surface_v1::Event::Configure {\n                serial,\n                width,\n                height,\n            } => {\n                let configure = LayerConfigure {\n                    size: (width, height),\n                };\n                layer_surface.configures_received.push((serial, configure));\n            }\n            zwlr_layer_surface_v1::Event::Closed => layer_surface.close_requested = true,\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<WlBuffer, ()> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &WlBuffer,\n        event: <WlBuffer as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        match event {\n            wl_buffer::Event::Release => (),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl Dispatch<WpSinglePixelBufferManagerV1, ()> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &WpSinglePixelBufferManagerV1,\n        _event: <WpSinglePixelBufferManagerV1 as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        unreachable!()\n    }\n}\n\nimpl Dispatch<WpViewporter, ()> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &WpViewporter,\n        _event: <WpViewporter as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        unreachable!()\n    }\n}\n\nimpl Dispatch<WpViewport, ()> for State {\n    fn event(\n        _state: &mut Self,\n        _proxy: &WpViewport,\n        _event: <WpViewport as wayland_client::Proxy>::Event,\n        _data: &(),\n        _conn: &Connection,\n        _qhandle: &QueueHandle<Self>,\n    ) {\n        unreachable!()\n    }\n}\n"
  },
  {
    "path": "src/tests/fixture.rs",
    "content": "use std::os::fd::AsFd as _;\nuse std::os::unix::net::UnixStream;\nuse std::sync::atomic::Ordering;\nuse std::time::Duration;\n\nuse calloop::generic::Generic;\nuse calloop::{EventLoop, Interest, LoopHandle, Mode, PostAction};\nuse niri_config::Config;\nuse smithay::output::Output;\n\nuse super::client::{Client, ClientId};\nuse super::server::Server;\nuse crate::niri::{NewClient, Niri};\n\npub struct Fixture {\n    pub event_loop: EventLoop<'static, State>,\n    pub handle: LoopHandle<'static, State>,\n    pub state: State,\n}\n\npub struct State {\n    pub server: Server,\n    pub clients: Vec<Client>,\n}\n\nimpl Fixture {\n    pub fn new() -> Self {\n        Self::with_config(Config::default())\n    }\n\n    pub fn with_config(config: Config) -> Self {\n        let event_loop = EventLoop::try_new().unwrap();\n        let handle = event_loop.handle();\n\n        let server = Server::new(config);\n        let fd = server.event_loop.as_fd().try_clone_to_owned().unwrap();\n        let source = Generic::new(fd, Interest::READ, Mode::Level);\n        handle\n            .insert_source(source, |_, _, state: &mut State| {\n                state.server.dispatch();\n                Ok(PostAction::Continue)\n            })\n            .unwrap();\n\n        let state = State {\n            server,\n            clients: Vec::new(),\n        };\n\n        Self {\n            event_loop,\n            handle,\n            state,\n        }\n    }\n\n    pub fn dispatch(&mut self) {\n        self.event_loop\n            .dispatch(Duration::ZERO, &mut self.state)\n            .unwrap();\n    }\n\n    pub fn niri_state(&mut self) -> &mut crate::niri::State {\n        &mut self.state.server.state\n    }\n\n    pub fn niri(&mut self) -> &mut Niri {\n        &mut self.niri_state().niri\n    }\n\n    pub fn niri_output(&self, n: u8) -> Output {\n        let niri = &self.state.server.state.niri;\n        let idx = usize::from(n - 1);\n        let output = niri.global_space.outputs().nth(idx).unwrap();\n        output.clone()\n    }\n\n    pub fn niri_focus_output(&mut self, n: u8) {\n        let niri = &mut self.state.server.state.niri;\n        let idx = usize::from(n - 1);\n        let output = niri.global_space.outputs().nth(idx).unwrap();\n        niri.layout.focus_output(output);\n    }\n\n    pub fn niri_complete_animations(&mut self) {\n        let niri = self.niri();\n        niri.clock.set_complete_instantly(true);\n        niri.advance_animations();\n        niri.clock.set_complete_instantly(false);\n    }\n\n    pub fn add_output(&mut self, n: u8, size: (u16, u16)) {\n        let state = self.niri_state();\n        let niri = &mut state.niri;\n        state.backend.headless().add_output(niri, n, size);\n    }\n\n    pub fn add_client(&mut self) -> ClientId {\n        let (sock1, sock2) = UnixStream::pair().unwrap();\n        self.niri().insert_client(NewClient {\n            client: sock1,\n            restricted: false,\n            credentials_unknown: false,\n        });\n\n        let client = Client::new(sock2);\n        let id = client.id;\n\n        let fd = client.event_loop.as_fd().try_clone_to_owned().unwrap();\n        let source = Generic::new(fd, Interest::READ, Mode::Level);\n        self.handle\n            .insert_source(source, move |_, _, state: &mut State| {\n                state.client(id).dispatch();\n                Ok(PostAction::Continue)\n            })\n            .unwrap();\n\n        self.state.clients.push(client);\n        self.roundtrip(id);\n        id\n    }\n\n    pub fn client(&mut self, id: ClientId) -> &mut Client {\n        self.state.client(id)\n    }\n\n    pub fn roundtrip(&mut self, id: ClientId) {\n        let client = self.state.client(id);\n        let data = client.send_sync();\n        while !data.done.load(Ordering::Relaxed) {\n            self.dispatch();\n        }\n    }\n\n    /// Roundtrip twice in a row.\n    ///\n    /// For some reason, when running tests on many threads at once, a single roundtrip is\n    /// sometimes not sufficient to get the configure events to the client.\n    ///\n    /// I suspect that this is because these configure events are sent from the niri loop callback,\n    /// so they arrive after the sync done event and don't get processed in that client dispatch\n    /// cycle. I'm not sure why this would be dependent on multithreading. But if this is indeed\n    /// the issue, then a double roundtrip fixes it.\n    pub fn double_roundtrip(&mut self, id: ClientId) {\n        self.roundtrip(id);\n        self.roundtrip(id);\n    }\n}\n\nimpl State {\n    pub fn client(&mut self, id: ClientId) -> &mut Client {\n        self.clients.iter_mut().find(|c| c.id == id).unwrap()\n    }\n}\n"
  },
  {
    "path": "src/tests/floating.rs",
    "content": "use client::ClientId;\nuse insta::assert_snapshot;\nuse niri_config::Config;\nuse niri_ipc::SizeChange;\nuse smithay::utils::Point;\nuse wayland_client::protocol::wl_surface::WlSurface;\n\nuse super::*;\n\n// Sets up a fixture with two outputs and 100×100 window.\nfn set_up() -> (Fixture, ClientId, WlSurface) {\n    set_up_with_config(Config::default())\n}\n\nfn set_up_with_config(config: Config) -> (Fixture, ClientId, WlSurface) {\n    let mut f = Fixture::with_config(config);\n    f.add_output(1, (1920, 1080));\n    f.add_output(2, (1280, 720));\n\n    let id = f.add_client();\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    window.attach_new_buffer();\n    window.set_size(100, 100);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    (f, id, surface)\n}\n\n#[test]\nfn unfocus_preserves_current_size() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.roundtrip(id);\n\n    // Change window size while it's floating.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Focus a different output which should drop the Activated state.\n    f.niri_focus_output(2);\n\n    f.double_roundtrip(id);\n\n    // This should request 200 × 200 because that's the current window size.\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 200 × 200, bounds: 1920 × 1080, states: []\"\n    );\n\n    // Change window size again.\n    let window = f.client(id).window(&surface);\n    window.set_size(300, 300);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Focus the first output which should add back the Activated state.\n    f.niri_focus_output(1);\n\n    f.double_roundtrip(id);\n\n    // This should request 300 × 300 because that's the current window size.\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 300 × 300, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn resize_to_different_size() {\n    let (mut f, id, surface) = set_up();\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Commit in response to the Activated state change configure.\n    f.client(id).window(&surface).ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    f.niri().layout.toggle_window_floating(None);\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n    f.double_roundtrip(id);\n\n    // This should request the new size, 500 × 100.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Focus a different output which should drop the Activated state.\n    f.niri_focus_output(2);\n    f.double_roundtrip(id);\n    // This should request the new size since the window hasn't committed yet.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 100, bounds: 1920 × 1080, states: []\"\n    );\n\n    // Ack but don't commit yet.\n    let window = f.client(id).window(&surface);\n    window.ack_last();\n    f.roundtrip(id);\n    // Add the activated state.\n    f.niri_focus_output(1);\n    f.double_roundtrip(id);\n    // This should request the new size since the window hasn't committed yet.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Commit but with some different size.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.commit();\n    f.double_roundtrip(id);\n    // This shouldn't request anything.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n\n    // Drop the Activated state.\n    f.niri_focus_output(2);\n    f.double_roundtrip(id);\n    // This should request the current window size rather than keep requesting 500 × 100.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 200, bounds: 1920 × 1080, states: []\"\n    );\n}\n\n#[test]\nfn set_window_width_uses_current_height() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Resize to something different on both axes.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Request a width change.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n\n    f.double_roundtrip(id);\n\n    // This should use the current window height (200), rather than the initial window height (100).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 200, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn set_window_height_uses_current_width() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Resize to something different on both axes.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Request a width change.\n    f.niri()\n        .layout\n        .set_window_height(None, SizeChange::SetFixed(500));\n\n    f.double_roundtrip(id);\n\n    // This should use the current window width (200), rather than the initial window width (100).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 500, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn resize_to_same_size() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Resize to something different.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Request a size change to the same size.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(200));\n\n    f.double_roundtrip(id);\n\n    // This needn't request anything because we're already that size; the size in the current\n    // server state matches the requested size.\n    //\n    // FIXME: However, currently it will request the size anyway because the code checks the\n    // current server state, and the last size niri requested of the window was 100×100 (even if\n    // the window already acked and committed in response).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 200, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn resize_to_different_then_same() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Commit in response to any configure from the floating change.\n    let window = f.client(id).window(&surface);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Request a size change to a different size.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n\n    f.double_roundtrip(id);\n\n    // This should request the new size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Before the window has a chance to respond, request a size change to the same, new size.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n\n    // And also drop the Activated state to have some pending change.\n    f.niri_focus_output(2);\n\n    f.double_roundtrip(id);\n\n    // This should keep requesting the new size (500 × 100) since the window has not responded to\n    // it yet.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 100, bounds: 1920 × 1080, states: []\"\n    );\n\n    // Commit in response to the size change request.\n    let window = f.client(id).window(&surface);\n    window.set_size(300, 300);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // And also add the Activated state to have some pending change.\n    f.niri_focus_output(1);\n\n    f.double_roundtrip(id);\n\n    // This should request the current window size (300 × 300) since the window has committed in\n    // response to the size change.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 300 × 300, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn restore_floating_size() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size while we're floating and commit in response to the floating configure.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Change back to tiling.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // We should get a tiling size configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n\n    // Resize as requested.\n    let window = f.client(id).window(&surface);\n    let (_, configure) = window.configures_received.last().unwrap();\n    window.set_size(configure.size.0 as u16, configure.size.1 as u16);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Change back to floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // We should get a configure restoring out previous 200 × 200 size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 200, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn moving_across_workspaces_doesnt_cancel_resize() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size while we're floating and commit in response to the floating configure.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Request a size change to a different size.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n    f.double_roundtrip(id);\n\n    // This should request the new size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 200, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Move to a different workspace before the window has a chance to respond. This will remove it\n    // from one floating layout and add into a different one, potentially causing a size request.\n    f.niri().layout.move_to_workspace_down(true);\n    // Drop the Activated state to force a configure.\n    f.niri_focus_output(2);\n    f.double_roundtrip(id);\n\n    // This should request the new size again (500 × 200) since the window hasn't responded to it.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 200, bounds: 1920 × 1080, states: []\"\n    );\n\n    // Respond to the resize with a different size.\n    let window = f.client(id).window(&surface);\n    window.set_size(300, 300);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Focus, adding Activated, and move to workspace down, causing removing and adding to a\n    // floating layout.\n    f.niri_focus_output(1);\n    f.niri().layout.move_to_workspace_down(true);\n    f.double_roundtrip(id);\n\n    // This should request the current size (300 × 300) since the window responded to the change.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 300 × 300, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn moving_to_floating_doesnt_cancel_resize() {\n    let (mut f, id, surface) = set_up();\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Request a size change to a different size.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n    f.double_roundtrip(id);\n\n    // This should request the new size (500 ×).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n\n    // Before the window has a chance to respond, make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should keep requesting the new size (500 ×).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 1048, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn interactive_move_unfullscreen_to_floating_restores_size() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size while we're floating and commit.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout.set_fullscreen(&window, true);\n    f.double_roundtrip(id);\n\n    // This should request a fullscreen size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Fullscreen]\"\n    );\n\n    // Start an interactive move which causes an unfullscreen into floating.\n    let output = f.niri_output(1);\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout\n        .interactive_move_begin(window.clone(), &output, Point::default());\n    niri.layout.interactive_move_update(\n        &window,\n        Point::from((1000., 0.)),\n        output,\n        Point::default(),\n    );\n    f.double_roundtrip(id);\n\n    // This should request the stored floating size (200 × 200).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 200, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn interactive_move_unmaximize_to_floating_restores_size() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size while we're floating and commit.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout.set_maximized(&window, true);\n    f.double_roundtrip(id);\n\n    // This should request a maximized size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]\"\n    );\n\n    // Start an interactive move which causes an unmaximize into floating.\n    let output = f.niri_output(1);\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout\n        .interactive_move_begin(window.clone(), &output, Point::default());\n    niri.layout.interactive_move_update(\n        &window,\n        Point::from((1000., 0.)),\n        output,\n        Point::default(),\n    );\n    f.double_roundtrip(id);\n\n    // This should request the stored floating size (200 × 200).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 200, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn resize_during_interactive_move_propagates_to_floating() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size while we're floating and commit.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Start an interactive move.\n    let output = f.niri_output(1);\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window_id = mapped.window.clone();\n    niri.layout\n        .interactive_move_begin(window_id.clone(), &output, Point::default());\n    niri.layout.interactive_move_update(\n        &window_id,\n        Point::from((1000., 0.)),\n        output,\n        Point::default(),\n    );\n    f.double_roundtrip(id);\n\n    // This shouldn't request any new size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n\n    // Change size while we're being interactively moved.\n    let window = f.client(id).window(&surface);\n    window.set_size(300, 300);\n    window.commit();\n    f.double_roundtrip(id);\n\n    // This shouldn't request any new size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n\n    // End the interactive move, placing the window into floating.\n    f.niri().layout.interactive_move_end(&window_id);\n    f.double_roundtrip(id);\n\n    // This should keep the new 300 × 300 size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 300 × 300, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn resize_in_steps() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Commit in response to the floating bounds state change configure.\n    f.client(id).window(&surface).ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    // Request a size change to a different size in two steps.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n    f.niri()\n        .layout\n        .set_window_height(None, SizeChange::SetFixed(500));\n    f.double_roundtrip(id);\n\n    // This should request the full new size (500 × 500) once.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 500, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    let window = f.client(id).window(&surface);\n    let serial = window.configures_received.last().unwrap().0;\n\n    // Request a size change now that the previous one is pending-but-not-acked.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(600));\n    // Drop Activated to work around resize throttling.\n    f.niri_focus_output(2);\n    f.double_roundtrip(id);\n\n    // This should request the new size (600 × 500) once.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 600 × 500, bounds: 1920 × 1080, states: []\"\n    );\n\n    // Commit in response to the previous configure.\n    let window = f.client(id).window(&surface);\n    window.xdg_surface.ack_configure(serial);\n    window.set_size(500, 500);\n    window.commit();\n\n    f.double_roundtrip(id);\n\n    // This shouldn't request anything.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n\n    // Request a height change now that the first one is committed-to, but the second isn't.\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    f.niri()\n        .layout\n        .set_window_height(Some(&window), SizeChange::SetFixed(600));\n    // Add Activated to work around resize throttling.\n    f.niri_focus_output(1);\n    f.double_roundtrip(id);\n\n    // This should request the latest sizes (600 × 600).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 600 × 600, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn state_change_doesnt_break_use_window_size() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Commit in response to the bounds change that comes with toggling floating.\n    f.client(id).window(&surface).ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Request a size change to a different size.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(500));\n    f.double_roundtrip(id);\n\n    // This should request the new size (500 × 100).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    let window = f.client(id).window(&surface);\n    let serial = window.configures_received.last().unwrap().0;\n\n    // Request a state change by dropping Activated.\n    f.niri_focus_output(2);\n    f.double_roundtrip(id);\n\n    // This should request the new size (500 × 100).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 500 × 100, bounds: 1920 × 1080, states: []\"\n    );\n\n    // Commit in response to the previous configure with a different size.\n    let window = f.client(id).window(&surface);\n    window.xdg_surface.ack_configure(serial);\n    window.set_size(300, 300);\n    window.commit();\n\n    f.double_roundtrip(id);\n\n    // This shouldn't request anything.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n\n    // Request a height change now that the first one is committed-to, but the second isn't.\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    f.niri()\n        .layout\n        .set_window_height(Some(&window), SizeChange::SetFixed(600));\n    // Add Activated state to force a configure.\n    f.niri_focus_output(1);\n    f.double_roundtrip(id);\n\n    // This should already request the current width (300 × 600) rather than the pending previous\n    // width (500 × 600) from the state change configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 300 × 600, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn interactive_move_restores_floating_size_when_set_to_floating() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size while we're floating and commit to make niri remember it.\n    let window = f.client(id).window(&surface);\n    window.set_size(200, 200);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Change back to tiling.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // We should get a tiled size configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n\n    // Resize as requested.\n    let window = f.client(id).window(&surface);\n    let (_, configure) = window.configures_received.last().unwrap();\n    window.set_size(configure.size.0 as u16, configure.size.1 as u16);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Start an interactive move.\n    let output = f.niri_output(1);\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window_id = mapped.window.clone();\n    niri.layout\n        .interactive_move_begin(window_id.clone(), &output, Point::default());\n    niri.layout.interactive_move_update(\n        &window_id,\n        Point::from((1000., 0.)),\n        output,\n        Point::default(),\n    );\n    f.double_roundtrip(id);\n\n    // This shouldn't request any new size because interactive move targets tiling.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 1048, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Change interactive move to target floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should restore the floating window size (200 × 200).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 200, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // End the interactive move, placing the window into floating.\n    f.niri().layout.interactive_move_end(&window_id);\n    f.double_roundtrip(id);\n\n    // This should keep the floating window size (200 × 200).\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n}\n\n#[test]\nfn floating_doesnt_store_fullscreen_size() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    f.add_output(2, (1280, 720));\n\n    // Open a window fullscreen.\n    let id = f.add_client();\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.set_fullscreen(None);\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    window.attach_new_buffer();\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should request 0 × 0 to unfullscreen.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 0 × 0, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Without committing, make it tiling again. We never committed while floating, so there's no\n    // floating size to remember.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should request the tiled size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n\n    // Commit in response.\n    let window = f.client(id).window(&surface);\n    window.set_size(100, 100);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Make the window floating again.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This shouldn't request any size change, particularly not the fullscreen size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 100 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn floating_doesnt_store_maximized_size() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    f.add_output(2, (1280, 720));\n\n    // Open a window maximized.\n    let id = f.add_client();\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.set_maximized();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    window.attach_new_buffer();\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should request 0 × 0 to unmaximize.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 0 × 0, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Without committing, make it tiling again. We never committed while floating, so there's no\n    // floating size to remember.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should request the tiled size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n\n    // Commit in response.\n    let window = f.client(id).window(&surface);\n    window.set_size(100, 100);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Make the window floating again.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This shouldn't request any size change, particularly not the maximized size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 100 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn floating_respects_non_fixed_min_max_rule() {\n    let config = r##\"\nwindow-rule {\n    min-width 200\n    max-width 300\n}\n\"##;\n    let config = Config::parse_mem(config).unwrap();\n    let (mut f, id, surface) = set_up_with_config(config);\n\n    // Commit to the Activated state configure.\n    f.client(id).window(&surface).ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should clamp to min-width and request 200 × 100.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Commit with a bigger width than max.\n    let window = f.client(id).window(&surface);\n    window.set_size(400, 100);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Make it tiling.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    f.client(id).window(&surface).ack_last_and_commit();\n    f.roundtrip(id);\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // This should clamp to max-width and request 300 × 100.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 300 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn unmap_from_floating() {\n    let (mut f, id, surface) = set_up();\n\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Resize to something different on both axes.\n    let window = f.client(id).window(&surface);\n    window.attach_null();\n    window.commit();\n\n    // Shouldn't panic.\n    f.double_roundtrip(id);\n}\n\n#[test]\nfn unfullscreen_to_floating_doesnt_send_extra_configure() {\n    let (mut f, id, surface) = set_up();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.roundtrip(id);\n\n    // Fullscreen.\n    let window = f.client(id).window(&surface);\n    window.set_fullscreen(None);\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Unfullscreen via the window request which requires a configure response.\n    let window = f.client(id).window(&surface);\n    window.unset_fullscreen();\n    f.double_roundtrip(id);\n\n    // This should configure only once and not twice.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn unmaximize_to_floating_doesnt_send_extra_configure() {\n    let (mut f, id, surface) = set_up();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.roundtrip(id);\n\n    // Maximize.\n    let window = f.client(id).window(&surface);\n    window.set_maximized();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Unmaximzie via the window request which requires a configure response.\n    let window = f.client(id).window(&surface);\n    window.unset_maximized();\n    f.double_roundtrip(id);\n\n    // This should configure only once and not twice.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn unfullscreen_to_same_size_floating() {\n    let (mut f, id, surface) = set_up();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size to the same as fullscreen, make niri remember it.\n    let window = f.client(id).window(&surface);\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Fullscreen.\n    let window = f.client(id).window(&surface);\n    window.set_fullscreen(None);\n    f.double_roundtrip(id);\n\n    // The fullscreen configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Fullscreen]\"\n    );\n\n    // Unfullscreen into floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // We should see a configure with the same size and no Fullscreen state.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn unmaximize_to_same_size_floating() {\n    let (mut f, id, surface) = set_up();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size to the same as maximized, make niri remember it.\n    let window = f.client(id).window(&surface);\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Maximize.\n    let window = f.client(id).window(&surface);\n    window.set_maximized();\n    f.double_roundtrip(id);\n\n    // The maximize configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]\"\n    );\n\n    // Unmaximize into floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // We should see a configure with the same size and no maximized state.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn unfullscreen_to_same_size_windowed_fullscreen_floating() {\n    let (mut f, id, surface) = set_up();\n\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    let window_id = mapped.window.clone();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size to the same as fullscreen, make niri remember it.\n    let window = f.client(id).window(&surface);\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Fullscreen.\n    let window = f.client(id).window(&surface);\n    window.set_fullscreen(None);\n    f.double_roundtrip(id);\n\n    // The fullscreen configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Fullscreen]\"\n    );\n\n    // Unfullscreen into windowed-fullscreen floating.\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.double_roundtrip(id);\n\n    // Should send configure because the bounds have changed.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated, Fullscreen]\"\n    );\n}\n\n#[test]\nfn unmaximize_to_same_size_windowed_fullscreen_floating() {\n    let (mut f, id, surface) = set_up();\n\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    let window_id = mapped.window.clone();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size to the same as maximized, make niri remember it.\n    let window = f.client(id).window(&surface);\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Maximize.\n    let window = f.client(id).window(&surface);\n    window.set_maximized();\n    f.double_roundtrip(id);\n\n    // The maximize configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]\"\n    );\n\n    // Enable windowed-fullscreen.\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.double_roundtrip(id);\n\n    // The windowed-fullscreen configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Fullscreen]\"\n    );\n\n    // Go back to windowed-fullscreen floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Should send configure because the bounds have changed.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated, Fullscreen]\"\n    );\n\n    // Disable windowed-fullscreen.\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.double_roundtrip(id);\n\n    // Should send configure dropping the Fullscreen state.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn unfullscreen_to_same_size_same_bounds_floating() {\n    let config = r##\"\nlayout {\n    gaps 0\n}\n\"##;\n    let config = Config::parse_mem(config).unwrap();\n    let (mut f, id, surface) = set_up_with_config(config);\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size to the same as fullscreen, make niri remember it.\n    let window = f.client(id).window(&surface);\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Fullscreen.\n    let window = f.client(id).window(&surface);\n    window.set_fullscreen(None);\n    f.double_roundtrip(id);\n\n    // The fullscreen configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated, Fullscreen]\"\n    );\n\n    // Unfullscreen into floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // We should see a configure with the same size and no Fullscreen state.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn unmaximize_to_same_size_same_bounds_floating() {\n    let config = r##\"\nlayout {\n    gaps 0\n}\n\"##;\n    let config = Config::parse_mem(config).unwrap();\n    let (mut f, id, surface) = set_up_with_config(config);\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // Change size to the same as fullscreen, make niri remember it.\n    let window = f.client(id).window(&surface);\n    window.set_size(1920, 1080);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Maximize.\n    let window = f.client(id).window(&surface);\n    window.set_maximized();\n    f.double_roundtrip(id);\n\n    // The maximize configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated, Maximized]\"\n    );\n\n    // Unmaximize into floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // We should see a configure with the same size and no Maximized state.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn repeated_size_request() {\n    let (mut f, id, surface) = set_up();\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    // Make it floating.\n    f.niri().layout.toggle_window_floating(None);\n    f.double_roundtrip(id);\n\n    // The floating configure.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Request a different width (200x100).\n    f.niri()\n        .layout\n        .set_window_width(None, SizeChange::SetFixed(200));\n    f.niri()\n        .layout\n        .set_window_height(None, SizeChange::SetFixed(100));\n    f.double_roundtrip(id);\n\n    // The 200x100 request.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 200 × 100, bounds: 1920 × 1080, states: [Activated]\"\n    );\n\n    // Request a size change to the same size as we have just requested.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(200));\n    f.double_roundtrip(id);\n\n    // Should request nothing as this is a repeated same-size request in floating and the surface\n    // hasn't committed to it yet.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n\n    // Ack but don't commit yet.\n    let window = f.client(id).window(&surface);\n    window.ack_last();\n    f.double_roundtrip(id);\n\n    // Request a size change to the same size as we have just requested.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(200));\n    f.double_roundtrip(id);\n\n    // Should request nothing as this is a repeated same-size request in floating and the surface\n    // hasn't committed to it yet.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n\n    // Commit.\n    let window = f.client(id).window(&surface);\n    window.commit();\n    f.double_roundtrip(id);\n\n    // Request the size change again.\n    f.niri().layout.set_column_width(SizeChange::SetFixed(200));\n    f.double_roundtrip(id);\n\n    // This should send a new configure since the window had committed.\n    //\n    // FIXME: doesn't request that currently.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"\"\n    );\n}\n"
  },
  {
    "path": "src/tests/fullscreen.rs",
    "content": "use client::ClientId;\nuse insta::assert_snapshot;\nuse smithay::utils::Point;\nuse wayland_client::protocol::wl_surface::WlSurface;\n\nuse super::*;\nuse crate::layout::LayoutElement as _;\n\n// Sets up a fixture with two outputs and 100×100 window.\nfn set_up() -> (Fixture, ClientId, WlSurface) {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    f.add_output(2, (1280, 720));\n\n    let id = f.add_client();\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    window.attach_new_buffer();\n    window.set_size(100, 100);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    (f, id, surface)\n}\n\n#[test]\nfn windowed_fullscreen() {\n    let (mut f, id, surface) = set_up();\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window_id = mapped.window.clone();\n\n    // Enable windowed fullscreen.\n    niri.layout.toggle_windowed_fullscreen(&window_id);\n    f.double_roundtrip(id);\n\n    // Should request fullscreen state with the tiled size.\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1888 × 1048, states: [Activated, Fullscreen]\"\n    );\n\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    // Not committed yet.\n    assert!(!mapped.is_windowed_fullscreen());\n\n    // Commit in response.\n    let window = f.client(id).window(&surface);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    // Now it is committed.\n    assert!(mapped.is_windowed_fullscreen());\n\n    // Disable windowed fullscreen.\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.double_roundtrip(id);\n\n    // Should request without fullscreen state with the tiled size.\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    // Not committed yet.\n    assert!(mapped.is_windowed_fullscreen());\n\n    // Commit in response.\n    let window = f.client(id).window(&surface);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    // Now it is committed.\n    assert!(!mapped.is_windowed_fullscreen());\n}\n\n#[test]\nfn windowed_fullscreen_chain() {\n    let (mut f, id, surface) = set_up();\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    let window_id = mapped.window.clone();\n\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.roundtrip(id);\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.roundtrip(id);\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.roundtrip(id);\n    f.niri().layout.toggle_windowed_fullscreen(&window_id);\n    f.double_roundtrip(id);\n\n    // Should be four configures matching the four requests.\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @r\"\n    size: 936 × 1048, bounds: 1888 × 1048, states: [Activated, Fullscreen]\n    size: 936 × 1048, bounds: 1888 × 1048, states: [Activated]\n    size: 936 × 1048, bounds: 1888 × 1048, states: [Activated, Fullscreen]\n    size: 936 × 1048, bounds: 1888 × 1048, states: [Activated]\n    \"\n    );\n\n    let window = f.client(id).window(&surface);\n    let serials = Vec::from_iter(\n        window.configures_received[window.configures_received.len() - 4..]\n            .iter()\n            .map(|(s, _c)| *s),\n    );\n\n    let get_state = |f: &mut Fixture| {\n        let mapped = f.niri().layout.windows().next().unwrap().1;\n        format!(\n            \"fs {}, wfs {}\",\n            mapped.sizing_mode().is_fullscreen(),\n            mapped.is_windowed_fullscreen()\n        )\n    };\n\n    let mut states = vec![get_state(&mut f)];\n    for serial in serials {\n        let window = f.client(id).window(&surface);\n        window.xdg_surface.ack_configure(serial);\n        window.commit();\n        f.roundtrip(id);\n        states.push(get_state(&mut f));\n    }\n\n    // We expect fs to always be false (because each Fullscreen state request corresponded to a\n    // windowed fullscreen), and wfs to toggle on and off.\n    assert_snapshot!(\n        states.join(\"\\n\"),\n        @r\"\n    fs false, wfs false\n    fs false, wfs true\n    fs false, wfs false\n    fs false, wfs true\n    fs false, wfs false\n    \"\n    );\n}\n\n#[test]\nfn unfullscreen_before_fullscreen_ack_doesnt_prevent_view_offset_save_restore() {\n    let (mut f, id, _surface) = set_up();\n\n    let window2 = f.client(id).create_window();\n    let surface2 = window2.surface.clone();\n    window2.commit();\n    f.roundtrip(id);\n\n    let window2 = f.client(id).window(&surface2);\n    window2.attach_new_buffer();\n    window2.set_size(200, 200);\n    window2.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface2).recent_configures();\n\n    let niri = f.niri();\n    let mapped2 = niri.layout.windows().last().unwrap().1;\n    let window2_id = mapped2.window.clone();\n\n    // The view position is at the first window.\n    assert_snapshot!(niri.layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n\n    // Fullscreen window2 and send the configure so we can clear pending.\n    niri.layout.set_fullscreen(&window2_id, true);\n    f.double_roundtrip(id);\n\n    // Before acking, unfullscreen the column, clearing the pending fullscreen flag.\n    f.niri().layout.set_fullscreen(&window2_id, false);\n\n    // Now, window2 receives the fullscreen configure and resizes in response.\n    let window2 = f.client(id).window(&surface2);\n    assert_snapshot!(\n        window2.format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Fullscreen]\"\n    );\n    let (_, configure) = window2.configures_received.last().unwrap();\n    window2.set_size(configure.size.0 as u16, configure.size.1 as u16);\n    window2.ack_last_and_commit();\n    f.double_roundtrip(id);\n    f.niri_complete_animations();\n\n    // The view position is now at the fullscreen-sized window2.\n    assert_snapshot!(f.niri().layout.active_workspace().unwrap().scrolling().view_pos(), @\"116\");\n\n    // Now, window2 receives the unfullscreen configure and resizes in response.\n    let window2 = f.client(id).window(&surface2);\n    assert_snapshot!(\n        window2.format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n    window2.set_size(200, 200);\n    window2.ack_last_and_commit();\n    f.roundtrip(id);\n    f.niri_complete_animations();\n\n    // The view position should restore to the first window.\n    assert_snapshot!(f.niri().layout.active_workspace().unwrap().scrolling().view_pos(), @\"-16\");\n}\n\n#[test]\nfn interactive_move_unfullscreen_to_scrolling_restores_size() {\n    let (mut f, id, surface) = set_up();\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout.set_fullscreen(&window, true);\n    f.double_roundtrip(id);\n\n    // This should request a fullscreen size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Fullscreen]\"\n    );\n\n    // Start an interactive move which causes an unfullscreen.\n    let output = f.niri_output(1);\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout\n        .interactive_move_begin(window.clone(), &output, Point::default());\n    niri.layout.interactive_move_update(\n        &window,\n        Point::from((1000., 0.)),\n        output,\n        Point::default(),\n    );\n    f.double_roundtrip(id);\n\n    // This should request the tiled size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n\n#[test]\nfn interactive_move_unmaximize_to_scrolling_restores_size() {\n    let (mut f, id, surface) = set_up();\n\n    let _ = f.client(id).window(&surface).recent_configures();\n\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout.set_maximized(&window, true);\n    f.double_roundtrip(id);\n\n    // This should request a maximized size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 1920 × 1080, bounds: 1888 × 1048, states: [Activated, Maximized]\"\n    );\n\n    // Start an interactive move which causes an unmaximize.\n    let output = f.niri_output(1);\n    let niri = f.niri();\n    let mapped = niri.layout.windows().next().unwrap().1;\n    let window = mapped.window.clone();\n    niri.layout\n        .interactive_move_begin(window.clone(), &output, Point::default());\n    niri.layout.interactive_move_update(\n        &window,\n        Point::from((1000., 0.)),\n        output,\n        Point::default(),\n    );\n    f.double_roundtrip(id);\n\n    // This should request the tiled size.\n    assert_snapshot!(\n        f.client(id).window(&surface).format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1920 × 1080, states: [Activated]\"\n    );\n}\n"
  },
  {
    "path": "src/tests/layer_shell.rs",
    "content": "use insta::assert_snapshot;\nuse smithay::reexports::wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_shell_v1::Layer;\nuse smithay::reexports::wayland_protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::{\n    Anchor, KeyboardInteractivity,\n};\n\nuse super::*;\nuse crate::tests::client::{LayerConfigureProps, LayerMargin};\n\n#[test]\nfn simple_top_anchor() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    let id = f.add_client();\n\n    let layer = f.client(id).create_layer(None, Layer::Top, \"\");\n    let surface = layer.surface.clone();\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 50)),\n        ..Default::default()\n    });\n    layer.commit();\n    f.roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    layer.attach_new_buffer();\n    layer.set_size(100, 100);\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 1920 × 50\");\n}\n\n#[test]\nfn margin_overflow() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    let id = f.add_client();\n\n    let layer = f.client(id).create_layer(None, Layer::Top, \"\");\n    let surface = layer.surface.clone();\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top | Anchor::Bottom),\n        margin: Some(LayerMargin {\n            top: i32::MAX,\n            right: i32::MAX,\n            bottom: i32::MAX,\n            left: i32::MAX,\n        }),\n        exclusive_zone: Some(i32::MAX),\n        ..Default::default()\n    });\n    layer.commit();\n    f.roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    layer.attach_new_buffer();\n    layer.set_size(100, 100);\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 0 × 0\");\n\n    // Add a second one for good measure.\n    let layer = f.client(id).create_layer(None, Layer::Top, \"\");\n    let surface = layer.surface.clone();\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top | Anchor::Bottom),\n        margin: Some(LayerMargin {\n            top: i32::MAX,\n            right: i32::MAX,\n            bottom: i32::MAX,\n            left: i32::MAX,\n        }),\n        exclusive_zone: Some(i32::MAX),\n        ..Default::default()\n    });\n    layer.commit();\n    f.roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    layer.attach_new_buffer();\n    layer.set_size(100, 100);\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 0 × 0\");\n}\n\n#[test]\nfn unmap_through_null_buffer() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    let id = f.add_client();\n\n    let layer = f.client(id).create_layer(None, Layer::Top, \"\");\n    let surface = layer.surface.clone();\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 50)),\n        ..Default::default()\n    });\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 1920 × 50\");\n\n    layer.attach_new_buffer();\n    layer.set_size(100, 100);\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // No new configure since nothing changed.\n    assert_snapshot!(layer.format_recent_configures(), @\"\");\n\n    // Unmap by attaching a null buffer. This moves the surface back to pre-initial-commit stage.\n    layer.attach_null();\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // Configures must be empty because we haven't done an initial commit yet.\n    assert_snapshot!(layer.format_recent_configures(), @\"\");\n\n    // Do the initial commit again.\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 100)),\n        ..Default::default()\n    });\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // This is the new initial configure.\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 1920 × 100\");\n\n    layer.attach_new_buffer();\n    layer.set_size(100, 100);\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    assert_snapshot!(layer.format_recent_configures(), @\"\");\n}\n\n#[test]\nfn multiple_commits_before_mapping() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    let id = f.add_client();\n\n    let layer = f.client(id).create_layer(None, Layer::Top, \"\");\n    let surface = layer.surface.clone();\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 50)),\n        ..Default::default()\n    });\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 1920 × 50\");\n\n    // Change something that won't cause a configure.\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 50)),\n        kb_interactivity: Some(KeyboardInteractivity::OnDemand),\n        ..Default::default()\n    });\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // No new configure since the size hasn't changed.\n    assert_snapshot!(layer.format_recent_configures(), @\"\");\n\n    // Change something that will cause a configure.\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 100)),\n        ..Default::default()\n    });\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // Configure with new size.\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 1920 × 100\");\n\n    // Map.\n    layer.attach_new_buffer();\n    layer.set_size(100, 100);\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // No new configure since nothing changed.\n    assert_snapshot!(layer.format_recent_configures(), @\"\");\n\n    // Unmap by attaching a null buffer. This moves the surface back to pre-initial-commit stage.\n    layer.attach_null();\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // Configures must be empty because we haven't done an initial commit yet.\n    assert_snapshot!(layer.format_recent_configures(), @\"\");\n\n    // Same configure props as before, but since we unmapped, we should get a new initial\n    // configure (that will happen to match the previous configure we had got while mapped).\n    let surface = layer.surface.clone();\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 100)),\n        ..Default::default()\n    });\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 1920 × 100\");\n\n    // Change something that won't cause a configure.\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 100)),\n        kb_interactivity: Some(KeyboardInteractivity::OnDemand),\n        ..Default::default()\n    });\n    layer.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // No new configure since the size hasn't changed.\n    assert_snapshot!(layer.format_recent_configures(), @\"\");\n\n    // Change something that will cause a configure.\n    layer.set_configure_props(LayerConfigureProps {\n        anchor: Some(Anchor::Left | Anchor::Right | Anchor::Top),\n        size: Some((0, 50)),\n        ..Default::default()\n    });\n    layer.commit();\n    f.double_roundtrip(id);\n\n    let layer = f.client(id).layer(&surface);\n    // Configure with new size.\n    assert_snapshot!(layer.format_recent_configures(), @\"size: 1920 × 50\");\n}\n"
  },
  {
    "path": "src/tests/mod.rs",
    "content": "use fixture::Fixture;\n\nmod client;\nmod fixture;\nmod server;\n\nmod animations;\nmod floating;\nmod fullscreen;\nmod layer_shell;\nmod transactions;\nmod window_opening;\n"
  },
  {
    "path": "src/tests/server.rs",
    "content": "use std::time::Duration;\n\nuse calloop::EventLoop;\nuse niri_config::Config;\nuse smithay::reexports::wayland_server::Display;\n\nuse crate::niri::State;\n\npub struct Server {\n    pub event_loop: EventLoop<'static, State>,\n    pub state: State,\n}\n\nimpl Server {\n    pub fn new(config: Config) -> Self {\n        let event_loop = EventLoop::try_new().unwrap();\n        let handle = event_loop.handle();\n        let display = Display::new().unwrap();\n        let state = State::new(\n            config,\n            handle.clone(),\n            event_loop.get_signal(),\n            display,\n            true,\n            false,\n            false,\n        )\n        .unwrap();\n\n        Self { event_loop, state }\n    }\n\n    pub fn dispatch(&mut self) {\n        self.event_loop\n            .dispatch(Duration::ZERO, &mut self.state)\n            .unwrap();\n        self.state.refresh_and_flush_clients();\n    }\n}\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-tmT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-tmT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized-to-edges false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@tmT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized-to-edges true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBN-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBN-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBN-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Activated, Maximized]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBN-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBU-wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: A\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBU-wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: AU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBU-wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: B\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBU-wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nwant maximized: BU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wmA.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: A\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wmAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: AU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wmB.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: B\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Maximized, Activated]\n\nunmaximize configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_fullscreen_maximize@wmBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want maximized: BU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@out2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-out2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-out2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"set parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-1\nfinal workspace: 0 (ws-1)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB1-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB1-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB1-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB1-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB2-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB2-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB2-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB2-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBN-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBN-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBN-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBN-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1900 × 1060, states: [Fullscreen]\n\npost-map configures:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 1920 × 1080, bounds: 1868 × 1028, states: [Fullscreen]\n\npost-map configures:\n\n\nunfullscreen configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBU-spA1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBU-spA2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: A2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBU-spB1.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B1\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-1\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBU-spB2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nset parent: B2\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\\n\\nwindow-rule {\\n    match title=\\\"parent\\\"\\n    open-on-output \\\"headless-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 0 × 0, bounds: 1900 × 1060, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1900 × 1060, states: []\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_output_and_workspace@ws2.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\noutput \\\"headless-2\\\" {\\n    layout {\\n        border {\\n            on\\n        }\\n    }\\n}\\n\\nworkspace \\\"ws-1\\\" {\\n    open-on-output \\\"headless-1\\\"\\n}\\n\\nworkspace \\\"ws-2\\\" {\\n    open-on-output \\\"headless-2\\\"\\n\\n    layout {\\n        border {\\n            width 10\\n        }\\n\\n        default-column-width {\\n            fixed 500\\n        }\\n    }\\n}\\n\\nwindow-rule {\\n    exclude title=\\\"parent\\\"\\n\\n    open-on-workspace \\\"ws-2\\\"\\n}\"\nexpression: snapshot\n---\nfinal monitor: headless-2\nfinal workspace: 0 (ws-2)\n\ninitial configure:\nsize: 500 × 1028, bounds: 1868 × 1028, states: []\n\npost-map configures:\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: []\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: []\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: []\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: []\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: []\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: []\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: []\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: []\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: []\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: []\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: []\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: []\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: []\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: []\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: []\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: []\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: []\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: []\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: []\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: []\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: []\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: []\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: []\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: []\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: []\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: []\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: []\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: []\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: []\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1231 × 680, states: []\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1240 × 680, states: []\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1231 × 680, states: []\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1240 × 680, states: []\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1239 × 688, states: []\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1248 × 688, states: []\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 328, bounds: 1231 × 680, states: []\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 328, bounds: 1240 × 680, states: []\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 336, bounds: 1239 × 688, states: []\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 336, bounds: 1248 × 688, states: []\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1231 × 680, states: []\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1240 × 680, states: []\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1239 × 688, states: []\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1248 × 688, states: []\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1239 × 688, states: []\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1248 × 688, states: []\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: []\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: []\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: []\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: []\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: []\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: []\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: []\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: []\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: []\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: []\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: []\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: []\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 599 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 608 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: []\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: []\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: []\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: []\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: []\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: []\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: []\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: []\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: []\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: []\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: []\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: []\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: []\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 283 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 292 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: []\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: []\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: []\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: []\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 291 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 300 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1231 × 680, states: []\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1240 × 680, states: []\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1231 × 680, states: []\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1240 × 680, states: []\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1239 × 688, states: []\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1248 × 688, states: []\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 328, bounds: 1231 × 680, states: []\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 328, bounds: 1240 × 680, states: []\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 336, bounds: 1239 × 688, states: []\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 336, bounds: 1248 × 688, states: []\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1231 × 680, states: []\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 0 × 680, bounds: 1240 × 680, states: []\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1239 × 688, states: []\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1248 × 688, states: []\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1239 × 688, states: []\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 0 × 688, bounds: 1248 × 688, states: []\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-ofT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-ofT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-omT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: []\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsF.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen false\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: []\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: []\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 599 × 500, bounds: 1231 × 680, states: []\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 608 × 500, bounds: 1240 × 680, states: []\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 607 × 500, bounds: 1239 × 688, states: []\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 500, bounds: 1248 × 688, states: []\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 599 × 328, bounds: 1231 × 680, states: []\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 608 × 328, bounds: 1240 × 680, states: []\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 607 × 336, bounds: 1239 × 688, states: []\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 336, bounds: 1248 × 688, states: []\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 599 × 680, bounds: 1231 × 680, states: []\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 608 × 680, bounds: 1240 × 680, states: []\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 599 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 608 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: []\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1231 × 680, states: []\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1240 × 680, states: []\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1239 × 688, states: []\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1248 × 688, states: []\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 328, bounds: 1231 × 680, states: []\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 328, bounds: 1240 × 680, states: []\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 336, bounds: 1239 × 688, states: []\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 336, bounds: 1248 × 688, states: []\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 680, bounds: 1231 × 680, states: []\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 680, bounds: 1240 × 680, states: []\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 688, bounds: 1239 × 688, states: []\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 688, bounds: 1248 × 688, states: []\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1000 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: []\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: []\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 283 × 500, bounds: 1231 × 680, states: []\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 292 × 500, bounds: 1240 × 680, states: []\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 291 × 500, bounds: 1239 × 688, states: []\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 300 × 500, bounds: 1248 × 688, states: []\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 283 × 328, bounds: 1231 × 680, states: []\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 292 × 328, bounds: 1240 × 680, states: []\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 291 × 336, bounds: 1239 × 688, states: []\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 300 × 336, bounds: 1248 × 688, states: []\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 283 × 680, bounds: 1231 × 680, states: []\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 292 × 680, bounds: 1240 × 680, states: []\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 283 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 292 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: []\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: []\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 291 × 688, bounds: 1239 × 688, states: []\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 300 × 688, bounds: 1248 × 688, states: []\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 291 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 300 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 680, bounds: 1231 × 680, states: []\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 680, bounds: 1240 × 680, states: []\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1231 × 680, states: []\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1240 × 680, states: []\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1239 × 688, states: []\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1248 × 688, states: []\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 328, bounds: 1231 × 680, states: []\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 328, bounds: 1240 × 680, states: []\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 336, bounds: 1239 × 688, states: []\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 336, bounds: 1248 × 688, states: []\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 680, bounds: 1231 × 680, states: []\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 680, bounds: 1240 × 680, states: []\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 688, bounds: 1239 × 688, states: []\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 688, bounds: 1248 × 688, states: []\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 688, bounds: 1239 × 688, states: []\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 688, bounds: 1248 × 688, states: []\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-ofT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-ofT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-omT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: []\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@fsT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-fullscreen true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@ofT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 500, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 500, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 500, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 500, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 500, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 500, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 500, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 500, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 328, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 328, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 328, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 328, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 336, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 336, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 336, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 336, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1231 × 680, bounds: 1231 × 680, states: []\n\npost-map configures:\nsize: 1231 × 680, bounds: 1231 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1240 × 680, bounds: 1240 × 680, states: []\n\npost-map configures:\nsize: 1240 × 680, bounds: 1240 × 680, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\nsize: 1000 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\nsize: 1000 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\nsize: 1000 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\nsize: 1000 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\nsize: 1000 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\nsize: 1000 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwF1000.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { fixed 1000; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1000 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 500, bounds: 1272 × 712, states: []\nsize: 312 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 500, bounds: 1280 × 720, states: []\nsize: 320 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 352, bounds: 1272 × 712, states: []\nsize: 312 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 360, bounds: 1280 × 720, states: []\nsize: 320 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 312 × 0, bounds: 1272 × 712, states: []\nsize: 312 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 312 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 320 × 0, bounds: 1280 × 720, states: []\nsize: 320 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwP0.25.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width { proportion 0.25; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 320 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1272 × 712, states: []\nsize: 1 × 500, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 500, bounds: 1280 × 720, states: []\nsize: 1 × 500, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhF500.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { fixed 500; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 500, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 352, bounds: 1272 × 712, states: []\nsize: 1 × 352, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 352, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 360, bounds: 1280 × 720, states: []\nsize: 1 × 360, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhP0.5.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height { proportion 0.5; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 360, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1272 × 712, states: []\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1231 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1272 × 712, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1240 × 680, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-b.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    border { on; }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1272 × 712, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1272 × 712, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-dhU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-window-height {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-dwU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-width {  }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 0 × 0, bounds: 1280 × 720, states: []\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1280 × 720, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 0 × 0, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-ofT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    open-floating true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 0 × 0, bounds: 1280 × 720, states: []\n\npost-map configures:\nsize: 1 × 1, bounds: 1280 × 720, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1239 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1239 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT-wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@omT.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    open-maximized true\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1248 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1248 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"config:\\nwindow-rule {\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsAN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsAN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AN\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsAU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: []\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsAU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: AU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: []\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsBN-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1239 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsBN.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BN\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen]\n\npost-map configures:\nsize: 1280 × 720, bounds: 1248 × 688, states: [Fullscreen, Activated]\n\nunfullscreen configure:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsBU-t.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n    default-column-display \\\"tabbed\\\"\\n}\\n\\nlayout {\\n    tab-indicator {\\n        place-within-column\\n    }\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 607 × 688, bounds: 1239 × 688, states: []\n\npost-map configures:\nsize: 607 × 688, bounds: 1239 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/snapshots/niri__tests__window_opening__check_target_size@wfsBU.snap",
    "content": "---\nsource: src/tests/window_opening.rs\ndescription: \"want fullscreen: BU\\nconfig:\\nwindow-rule {\\n}\"\nexpression: snapshot\n---\ninitial configure:\nsize: 616 × 688, bounds: 1248 × 688, states: []\n\npost-map configures:\nsize: 616 × 688, bounds: 1248 × 688, states: [Activated]\n"
  },
  {
    "path": "src/tests/transactions.rs",
    "content": "use std::fmt::Write as _;\n\nuse insta::assert_snapshot;\nuse niri_ipc::SizeChange;\nuse wayland_client::protocol::wl_surface::WlSurface;\n\nuse super::client::ClientId;\nuse super::*;\nuse crate::layout::LayoutElement;\nuse crate::niri::Niri;\n\nfn format_window_sizes(niri: &Niri) -> String {\n    let mut buf = String::new();\n    for (_out, mapped) in niri.layout.windows() {\n        let size = mapped.size();\n        writeln!(&mut buf, \"{} × {}\", size.w, size.h).unwrap();\n    }\n    buf\n}\n\nfn create_window(f: &mut Fixture, id: ClientId, w: u16, h: u16) -> WlSurface {\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    window.attach_new_buffer();\n    window.set_size(w, h);\n    window.ack_last_and_commit();\n    f.roundtrip(id);\n\n    surface\n}\n\n#[test]\nfn column_resize_waits_for_both_windows() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n    let id = f.add_client();\n\n    let surface1 = create_window(&mut f, id, 100, 100);\n    let surface2 = create_window(&mut f, id, 200, 200);\n    f.double_roundtrip(id);\n\n    let _ = f.client(id).window(&surface1).recent_configures();\n    let _ = f.client(id).window(&surface2).recent_configures();\n\n    // Consume into one column.\n    f.niri().layout.consume_or_expel_window_left(None);\n    f.double_roundtrip(id);\n\n    // Commit for the column consume.\n    let window = f.client(id).window(&surface1);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 936 × 516, bounds: 1888 × 1048, states: []\"\n    );\n    window.ack_last_and_commit();\n\n    let window = f.client(id).window(&surface2);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 936 × 516, bounds: 1888 × 1048, states: [Activated]\"\n    );\n    window.ack_last_and_commit();\n\n    f.double_roundtrip(id);\n\n    // This should say 100 × 100 and 200 × 200.\n    assert_snapshot!(format_window_sizes(f.niri()), @r\"\n    100 × 100\n    200 × 200\n    \");\n\n    // Issue a resize.\n    f.niri()\n        .layout\n        .set_column_width(SizeChange::AdjustFixed(10));\n    f.double_roundtrip(id);\n\n    // Commit window 1 in response to resize.\n    let window = f.client(id).window(&surface1);\n    window.set_size(300, 300);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    // This should still say 100 × 100 as we're waiting in a transaction for the second window.\n    assert_snapshot!(format_window_sizes(f.niri()), @r\"\n    100 × 100\n    200 × 200\n    \");\n\n    // Commit window 2 in response to resize.\n    let window = f.client(id).window(&surface2);\n    window.set_size(400, 400);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    // This should say 300 × 300 and 400 × 400 as the transaction completed.\n    assert_snapshot!(format_window_sizes(f.niri()), @r\"\n    300 × 300\n    400 × 400\n    \");\n}\n"
  },
  {
    "path": "src/tests/window_opening.rs",
    "content": "use std::fmt::{self, Write as _};\n\nuse insta::assert_snapshot;\nuse niri_config::Config;\nuse rayon::iter::{IntoParallelIterator, ParallelIterator};\n\nuse super::*;\nuse crate::layout::LayoutElement as _;\nuse crate::utils::spawning::store_and_increase_nofile_rlimit;\nuse crate::utils::with_toplevel_role;\n\n#[test]\nfn simple_no_workspaces() {\n    let mut f = Fixture::new();\n\n    let id = f.add_client();\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 0 × 0, bounds: 0 × 0, states: []\"\n    );\n\n    window.attach_new_buffer();\n    window.set_size(100, 100);\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 100 × 688, bounds: 1248 × 688, states: []\"\n    );\n}\n\n#[test]\nfn simple() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n\n    let id = f.add_client();\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1888 × 1048, states: []\"\n    );\n\n    window.attach_new_buffer();\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    assert_snapshot!(\n        window.format_recent_configures(),\n        @\"size: 936 × 1048, bounds: 1888 × 1048, states: [Activated]\"\n    );\n}\n\n#[test]\n#[should_panic(expected = \"Protocol error 3 on object xdg_surface\")]\nfn dont_ack_initial_configure() {\n    let mut f = Fixture::new();\n    f.add_output(1, (1920, 1080));\n\n    let id = f.add_client();\n    let window = f.client(id).create_window();\n    let surface = window.surface.clone();\n    window.commit();\n    f.roundtrip(id);\n\n    let window = f.client(id).window(&surface);\n    window.attach_new_buffer();\n    // Don't ack the configure.\n    window.commit();\n    f.double_roundtrip(id);\n}\n\n#[derive(Clone, Copy)]\nenum WantFullscreen {\n    No,\n    UnsetBeforeInitial,\n    BeforeInitial(Option<&'static str>),\n    UnsetAfterInitial,\n    AfterInitial(Option<&'static str>),\n}\n\nimpl fmt::Display for WantFullscreen {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            WantFullscreen::No => write!(f, \"U\")?,\n            WantFullscreen::UnsetBeforeInitial => write!(f, \"BU\")?,\n            WantFullscreen::UnsetAfterInitial => write!(f, \"AU\")?,\n            WantFullscreen::BeforeInitial(m) => write!(f, \"B{}\", m.unwrap_or(\"N\"))?,\n            WantFullscreen::AfterInitial(m) => write!(f, \"A{}\", m.unwrap_or(\"N\"))?,\n        }\n        Ok(())\n    }\n}\n\n#[derive(Clone, Copy)]\nenum WantMaximized {\n    No,\n    UnsetBeforeInitial,\n    BeforeInitial,\n    UnsetAfterInitial,\n    AfterInitial,\n}\n\nimpl fmt::Display for WantMaximized {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            WantMaximized::No => write!(f, \"U\")?,\n            WantMaximized::UnsetBeforeInitial => write!(f, \"BU\")?,\n            WantMaximized::UnsetAfterInitial => write!(f, \"AU\")?,\n            WantMaximized::BeforeInitial => write!(f, \"B\")?,\n            WantMaximized::AfterInitial => write!(f, \"A\")?,\n        }\n        Ok(())\n    }\n}\n\n#[derive(Clone, Copy)]\nenum SetParent {\n    BeforeInitial(&'static str),\n    AfterInitial(&'static str),\n}\n\nimpl fmt::Display for SetParent {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            SetParent::BeforeInitial(m) => write!(f, \"B{m}\")?,\n            SetParent::AfterInitial(m) => write!(f, \"A{m}\")?,\n        }\n        Ok(())\n    }\n}\n\n#[derive(Clone, Copy)]\nenum DefaultSize {\n    WindowChooses,\n    Proportion(&'static str),\n    Fixed(&'static str),\n}\n\nimpl fmt::Display for DefaultSize {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            DefaultSize::WindowChooses => write!(f, \"U\"),\n            DefaultSize::Proportion(prop) => write!(f, \"P{prop}\"),\n            DefaultSize::Fixed(fixed) => write!(f, \"F{fixed}\"),\n        }\n    }\n}\n\n#[test]\nfn target_output_and_workspaces() {\n    store_and_increase_nofile_rlimit();\n\n    // Here we test a massive powerset of settings that can affect where a window opens:\n    //\n    // * open-on-workspace\n    // * open-on-output\n    // * has parent (windows will open next to their parent)\n    // * want fullscreen (windows can request the target fullscreen output)\n    // * open-fullscreen (can deny the fullscreen request)\n\n    let open_on_workspace = [None, Some(\"1\"), Some(\"2\")];\n    let open_on_output = [None, Some(\"1\"), Some(\"2\")];\n    let open_fullscreen = [None, Some(\"false\"), Some(\"true\")];\n    let want_fullscreen = [\n        WantFullscreen::No,\n        WantFullscreen::UnsetBeforeInitial, // GTK 4\n        WantFullscreen::BeforeInitial(None),\n        WantFullscreen::BeforeInitial(Some(\"1\")),\n        WantFullscreen::BeforeInitial(Some(\"2\")),\n        WantFullscreen::UnsetAfterInitial,\n        // mpv, osu!\n        WantFullscreen::AfterInitial(None),\n        WantFullscreen::AfterInitial(Some(\"1\")),\n        WantFullscreen::AfterInitial(Some(\"2\")),\n    ];\n    let set_parent = [\n        None,\n        Some(SetParent::BeforeInitial(\"1\")),\n        Some(SetParent::BeforeInitial(\"2\")),\n        Some(SetParent::AfterInitial(\"1\")),\n        Some(SetParent::AfterInitial(\"2\")),\n    ];\n\n    let mut powerset = Vec::new();\n    for ws in open_on_workspace {\n        for out in open_on_output {\n            for fs in open_fullscreen {\n                for wfs in want_fullscreen {\n                    for sp in set_parent {\n                        powerset.push((ws, out, fs, wfs, sp));\n                    }\n                }\n            }\n        }\n    }\n\n    powerset.into_par_iter().for_each(|(ws, out, fs, wfs, sp)| {\n        check_target_output_and_workspace(ws, out, fs, wfs, sp);\n    });\n}\n\nfn check_target_output_and_workspace(\n    open_on_workspace: Option<&str>,\n    open_on_output: Option<&str>,\n    open_fullscreen: Option<&str>,\n    want_fullscreen: WantFullscreen,\n    set_parent: Option<SetParent>,\n) {\n    let mut snapshot_desc = Vec::new();\n    let mut snapshot_suffix = Vec::new();\n\n    let mut config = String::from(\n        r##\"\noutput \"headless-2\" {\n    layout {\n        border {\n            on\n        }\n    }\n}\n\nworkspace \"ws-1\" {\n    open-on-output \"headless-1\"\n}\n\nworkspace \"ws-2\" {\n    open-on-output \"headless-2\"\n\n    layout {\n        border {\n            width 10\n        }\n\n        default-column-width {\n            fixed 500\n        }\n    }\n}\n\nwindow-rule {\n    exclude title=\"parent\"\n\n\"##,\n    );\n\n    if let Some(x) = open_on_workspace {\n        writeln!(config, \"    open-on-workspace \\\"ws-{x}\\\"\").unwrap();\n        snapshot_suffix.push(format!(\"ws{x}\"));\n    }\n\n    if let Some(x) = open_on_output {\n        writeln!(config, \"    open-on-output \\\"headless-{x}\\\"\").unwrap();\n        snapshot_suffix.push(format!(\"out{x}\"));\n    }\n\n    if let Some(x) = open_fullscreen {\n        writeln!(config, \"    open-fullscreen {x}\").unwrap();\n\n        let x = if x == \"true\" { \"T\" } else { \"F\" };\n        snapshot_suffix.push(format!(\"fs{x}\"));\n    }\n    config.push('}');\n\n    match &want_fullscreen {\n        WantFullscreen::No => (),\n        x => {\n            snapshot_desc.push(format!(\"want fullscreen: {x}\"));\n            snapshot_suffix.push(format!(\"wfs{x}\"));\n        }\n    }\n\n    if let Some(set_parent) = set_parent {\n        let mon = match set_parent {\n            SetParent::BeforeInitial(mon) => mon,\n            SetParent::AfterInitial(mon) => mon,\n        };\n        write!(\n            config,\n            \"\n\nwindow-rule {{\n    match title=\\\"parent\\\"\n    open-on-output \\\"headless-{mon}\\\"\n}}\"\n        )\n        .unwrap();\n\n        snapshot_desc.push(format!(\"set parent: {set_parent}\"));\n        snapshot_suffix.push(format!(\"sp{set_parent}\"));\n    }\n\n    snapshot_desc.push(format!(\"config:{config}\"));\n\n    let config = Config::parse_mem(&config).unwrap();\n\n    let mut f = Fixture::with_config(config);\n    f.add_output(1, (1280, 720));\n    f.add_output(2, (1920, 1080));\n\n    let id = f.add_client();\n\n    // To get output names.\n    f.roundtrip(id);\n\n    let mut parent = None;\n    if set_parent.is_some() {\n        let window = f.client(id).create_window();\n        let surface = window.surface.clone();\n        parent = Some(window.xdg_toplevel.clone());\n        window.set_title(\"parent\");\n        window.commit();\n        f.roundtrip(id);\n\n        let window = f.client(id).window(&surface);\n        window.attach_new_buffer();\n        window.ack_last_and_commit();\n        f.roundtrip(id);\n    }\n\n    let client = f.client(id);\n    let window = client.create_window();\n    let surface = window.surface.clone();\n\n    if let Some(SetParent::BeforeInitial(_)) = set_parent {\n        client.window(&surface).set_parent(parent.as_ref());\n    }\n\n    if let WantFullscreen::UnsetBeforeInitial = want_fullscreen {\n        client.window(&surface).unset_fullscreen();\n    } else if let WantFullscreen::BeforeInitial(mon) = want_fullscreen {\n        let output = mon.map(|mon| client.output(&format!(\"headless-{mon}\")));\n        client.window(&surface).set_fullscreen(output.as_ref());\n    }\n\n    client.window(&surface).commit();\n    f.roundtrip(id);\n\n    let client = f.client(id);\n    let initial = client.window(&surface).format_recent_configures();\n\n    if let Some(SetParent::AfterInitial(_)) = set_parent {\n        client.window(&surface).set_parent(parent.as_ref());\n    }\n\n    if let WantFullscreen::UnsetAfterInitial = want_fullscreen {\n        client.window(&surface).unset_fullscreen();\n    } else if let WantFullscreen::AfterInitial(mon) = want_fullscreen {\n        let output = mon.map(|mon| client.output(&format!(\"headless-{mon}\")));\n        client.window(&surface).set_fullscreen(output.as_ref());\n    }\n\n    let window = client.window(&surface);\n    window.attach_new_buffer();\n    let serial = window.configures_received.last().unwrap().0;\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    // Commit to the post-initial configures.\n    let window = f.client(id).window(&surface);\n    let new_serial = window.configures_received.last().unwrap().0;\n    if new_serial != serial {\n        window.ack_last_and_commit();\n        f.double_roundtrip(id);\n    }\n\n    let niri = f.niri();\n    let (mon, ws_idx, ws, mapped) = niri\n        .layout\n        .workspaces()\n        .find_map(|(mon, ws_idx, ws)| {\n            ws.windows().find_map(|win| {\n                if with_toplevel_role(win.toplevel(), |role| {\n                    role.title.as_deref() != Some(\"parent\")\n                }) {\n                    Some((mon, ws_idx, ws, win))\n                } else {\n                    None\n                }\n            })\n        })\n        .unwrap();\n    let is_fullscreen = mapped.sizing_mode().is_fullscreen();\n    let win = mapped.window.clone();\n    let mon = mon.unwrap().output_name().clone();\n    let ws = ws.name().cloned().unwrap_or(String::from(\"unnamed\"));\n\n    let window = f.client(id).window(&surface);\n    let post_map = window.format_recent_configures();\n\n    // If the window ended up fullscreen, unfullscreen it and output the configure.\n    let mut post_unfullscreen = String::new();\n    if is_fullscreen {\n        f.niri().layout.set_fullscreen(&win, false);\n        f.double_roundtrip(id);\n\n        let window = f.client(id).window(&surface);\n        post_unfullscreen = format!(\n            \"\\n\\nunfullscreen configure:\\n{}\",\n            window.format_recent_configures()\n        );\n    }\n\n    let snapshot = format!(\n        \"\\\nfinal monitor: {mon}\nfinal workspace: {ws_idx} ({ws})\n\ninitial configure:\n{initial}\n\npost-map configures:\n{post_map}{post_unfullscreen}\",\n    );\n\n    let mut settings = insta::Settings::clone_current();\n    settings.set_snapshot_suffix(snapshot_suffix.join(\"-\"));\n    settings.set_description(snapshot_desc.join(\"\\n\"));\n    let _guard = settings.bind_to_scope();\n    assert_snapshot!(snapshot);\n}\n\n#[test]\nfn target_size() {\n    if std::env::var_os(\"RUN_SLOW_TESTS\").is_none() {\n        eprintln!(\"ignoring slow test\");\n        return;\n    }\n\n    store_and_increase_nofile_rlimit();\n\n    // Here we test a massive powerset of settings that can affect the window size:\n    //\n    // * want fullscreen\n    // * open-fullscreen\n    // * open-maximized\n    // * open-floating\n    // * default-column-width\n    // * border\n    // * default-column-display normal, tabbed\n\n    let open_fullscreen = [None, Some(\"false\"), Some(\"true\")];\n    let want_fullscreen = [\n        WantFullscreen::No,\n        WantFullscreen::UnsetBeforeInitial, // GTK 4\n        WantFullscreen::BeforeInitial(None),\n        WantFullscreen::UnsetAfterInitial,\n        // mpv, osu!\n        WantFullscreen::AfterInitial(None),\n    ];\n    let open_maximized = [None, Some(\"true\")];\n    let open_floating = [None, Some(\"true\")];\n    let default_column_width = [\n        None,\n        Some(DefaultSize::WindowChooses),\n        Some(DefaultSize::Proportion(\"0.25\")),\n        Some(DefaultSize::Fixed(\"1000\")),\n    ];\n    let default_window_height = [\n        None,\n        Some(DefaultSize::WindowChooses),\n        Some(DefaultSize::Proportion(\"0.5\")),\n        Some(DefaultSize::Fixed(\"500\")),\n    ];\n    let border = [false, true];\n    let tabbed = [false, true];\n\n    let mut powerset = Vec::new();\n    for fs in open_fullscreen {\n        for wfs in want_fullscreen {\n            for om in open_maximized {\n                for of in open_floating {\n                    for dw in default_column_width {\n                        for dh in default_window_height {\n                            for b in border {\n                                for t in tabbed {\n                                    powerset.push((fs, wfs, om, of, dw, dh, b, t));\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    powerset\n        .into_par_iter()\n        .for_each(|(fs, wfs, om, of, dw, dh, b, t)| {\n            check_target_size(fs, wfs, om, of, dw, dh, b, t);\n        });\n}\n\n#[allow(clippy::too_many_arguments)]\nfn check_target_size(\n    open_fullscreen: Option<&str>,\n    want_fullscreen: WantFullscreen,\n    open_maximized: Option<&str>,\n    open_floating: Option<&str>,\n    default_width: Option<DefaultSize>,\n    default_height: Option<DefaultSize>,\n    border: bool,\n    tabbed: bool,\n) {\n    let mut snapshot_desc = Vec::new();\n    let mut snapshot_suffix = Vec::new();\n\n    let mut config = String::from(\n        r##\"\nwindow-rule {\n\"##,\n    );\n\n    if let Some(x) = open_fullscreen {\n        writeln!(config, \"    open-fullscreen {x}\").unwrap();\n\n        let x = if x == \"true\" { \"T\" } else { \"F\" };\n        snapshot_suffix.push(format!(\"fs{x}\"));\n    }\n\n    if let Some(x) = open_maximized {\n        writeln!(config, \"    open-maximized {x}\").unwrap();\n\n        let x = if x == \"true\" { \"T\" } else { \"F\" };\n        snapshot_suffix.push(format!(\"om{x}\"));\n    }\n\n    if let Some(x) = open_floating {\n        writeln!(config, \"    open-floating {x}\").unwrap();\n\n        let x = if x == \"true\" { \"T\" } else { \"F\" };\n        snapshot_suffix.push(format!(\"of{x}\"));\n    }\n\n    if let Some(x) = default_width {\n        let value = match x {\n            DefaultSize::WindowChooses => String::new(),\n            DefaultSize::Proportion(prop) => format!(\"proportion {prop};\"),\n            DefaultSize::Fixed(fixed) => format!(\"fixed {fixed};\"),\n        };\n        writeln!(config, \"    default-column-width {{ {value} }}\").unwrap();\n\n        snapshot_suffix.push(format!(\"dw{x}\"));\n    }\n\n    if let Some(x) = default_height {\n        let value = match x {\n            DefaultSize::WindowChooses => String::new(),\n            DefaultSize::Proportion(prop) => format!(\"proportion {prop};\"),\n            DefaultSize::Fixed(fixed) => format!(\"fixed {fixed};\"),\n        };\n        writeln!(config, \"    default-window-height {{ {value} }}\").unwrap();\n\n        snapshot_suffix.push(format!(\"dh{x}\"));\n    }\n\n    if border {\n        writeln!(config, \"    border {{ on; }}\").unwrap();\n        snapshot_suffix.push(String::from(\"b\"));\n    }\n\n    if tabbed {\n        writeln!(config, \"    default-column-display \\\"tabbed\\\"\").unwrap();\n    }\n\n    config.push('}');\n\n    match &want_fullscreen {\n        WantFullscreen::No => (),\n        x => {\n            snapshot_desc.push(format!(\"want fullscreen: {x}\"));\n            snapshot_suffix.push(format!(\"wfs{x}\"));\n        }\n    }\n\n    if tabbed {\n        config.push_str(\n            \"\\n\nlayout {\n    tab-indicator {\n        place-within-column\n    }\n}\",\n        );\n        snapshot_suffix.push(String::from(\"t\"));\n    }\n\n    snapshot_desc.push(format!(\"config:{config}\"));\n\n    let config = Config::parse_mem(&config).unwrap();\n\n    let mut f = Fixture::with_config(config);\n    f.add_output(1, (1280, 720));\n    f.add_output(2, (1920, 1080));\n\n    let id = f.add_client();\n\n    // To get output names.\n    f.roundtrip(id);\n\n    let client = f.client(id);\n    let window = client.create_window();\n    let surface = window.surface.clone();\n\n    if let WantFullscreen::UnsetBeforeInitial = want_fullscreen {\n        client.window(&surface).unset_fullscreen();\n    } else if let WantFullscreen::BeforeInitial(mon) = want_fullscreen {\n        let output = mon.map(|mon| client.output(&format!(\"headless-{mon}\")));\n        client.window(&surface).set_fullscreen(output.as_ref());\n    }\n\n    client.window(&surface).commit();\n    f.roundtrip(id);\n\n    let client = f.client(id);\n    let initial = client.window(&surface).format_recent_configures();\n\n    if let WantFullscreen::UnsetAfterInitial = want_fullscreen {\n        client.window(&surface).unset_fullscreen();\n    } else if let WantFullscreen::AfterInitial(mon) = want_fullscreen {\n        let output = mon.map(|mon| client.output(&format!(\"headless-{mon}\")));\n        client.window(&surface).set_fullscreen(output.as_ref());\n    }\n\n    let window = client.window(&surface);\n    window.attach_new_buffer();\n    let serial = window.configures_received.last().unwrap().0;\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    // Commit to the post-initial configures.\n    let window = f.client(id).window(&surface);\n    let new_serial = window.configures_received.last().unwrap().0;\n    if new_serial != serial {\n        window.ack_last_and_commit();\n        f.double_roundtrip(id);\n    }\n\n    let window = f.client(id).window(&surface);\n    let post_map = window.format_recent_configures();\n\n    // If the window ended up fullscreen, unfullscreen it and output the configure.\n    let mut post_unfullscreen = String::new();\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    let is_fullscreen = mapped.sizing_mode().is_fullscreen();\n    let win = mapped.window.clone();\n    if is_fullscreen {\n        f.niri().layout.set_fullscreen(&win, false);\n        f.double_roundtrip(id);\n\n        let window = f.client(id).window(&surface);\n        post_unfullscreen = format!(\n            \"\\n\\nunfullscreen configure:\\n{}\",\n            window.format_recent_configures()\n        );\n    }\n\n    let snapshot = format!(\n        \"\\\ninitial configure:\n{initial}\n\npost-map configures:\n{post_map}{post_unfullscreen}\",\n    );\n\n    let mut settings = insta::Settings::clone_current();\n    settings.set_snapshot_suffix(snapshot_suffix.join(\"-\"));\n    settings.set_description(snapshot_desc.join(\"\\n\"));\n    let _guard = settings.bind_to_scope();\n    assert_snapshot!(snapshot);\n}\n\n#[test]\nfn fullscreen_maximize() {\n    store_and_increase_nofile_rlimit();\n\n    let open_fullscreen = [None, Some(\"false\"), Some(\"true\")];\n    let want_fullscreen = [\n        WantFullscreen::No,\n        WantFullscreen::UnsetBeforeInitial, // GTK 4\n        WantFullscreen::BeforeInitial(None),\n        WantFullscreen::UnsetAfterInitial,\n        // mpv, osu!\n        WantFullscreen::AfterInitial(None),\n    ];\n    let open_maximized = [None, Some(\"false\"), Some(\"true\")];\n    let want_maximized = [\n        WantMaximized::No,\n        WantMaximized::UnsetBeforeInitial,\n        WantMaximized::BeforeInitial,\n        WantMaximized::UnsetAfterInitial,\n        WantMaximized::AfterInitial,\n    ];\n\n    let mut powerset = Vec::new();\n    for fs in open_fullscreen {\n        for wfs in want_fullscreen {\n            for tm in open_maximized {\n                for wm in want_maximized {\n                    powerset.push((fs, wfs, tm, wm));\n                }\n            }\n        }\n    }\n\n    powerset.into_par_iter().for_each(|(fs, wfs, tm, wm)| {\n        check_fullscreen_maximize(fs, wfs, tm, wm);\n    });\n}\n\nfn check_fullscreen_maximize(\n    open_fullscreen: Option<&str>,\n    want_fullscreen: WantFullscreen,\n    open_maximized: Option<&str>,\n    want_maximized: WantMaximized,\n) {\n    let mut snapshot_desc = Vec::new();\n    let mut snapshot_suffix = Vec::new();\n\n    let mut config = String::from(\n        r##\"\nwindow-rule {\n\"##,\n    );\n\n    if let Some(x) = open_fullscreen {\n        writeln!(config, \"    open-fullscreen {x}\").unwrap();\n\n        let x = if x == \"true\" { \"T\" } else { \"F\" };\n        snapshot_suffix.push(format!(\"fs{x}\"));\n    }\n\n    if let Some(x) = open_maximized {\n        writeln!(config, \"    open-maximized-to-edges {x}\").unwrap();\n\n        let x = if x == \"true\" { \"T\" } else { \"F\" };\n        snapshot_suffix.push(format!(\"tm{x}\"));\n    }\n\n    config.push('}');\n\n    match &want_fullscreen {\n        WantFullscreen::No => (),\n        x => {\n            snapshot_desc.push(format!(\"want fullscreen: {x}\"));\n            snapshot_suffix.push(format!(\"wfs{x}\"));\n        }\n    }\n\n    match &want_maximized {\n        WantMaximized::No => (),\n        x => {\n            snapshot_desc.push(format!(\"want maximized: {x}\"));\n            snapshot_suffix.push(format!(\"wm{x}\"));\n        }\n    }\n\n    snapshot_desc.push(format!(\"config:{config}\"));\n\n    let config = Config::parse_mem(&config).unwrap();\n\n    let mut f = Fixture::with_config(config);\n    f.add_output(1, (1280, 720));\n    f.add_output(2, (1920, 1080));\n\n    let id = f.add_client();\n\n    // To get output names.\n    f.roundtrip(id);\n\n    let client = f.client(id);\n    let window = client.create_window();\n    let surface = window.surface.clone();\n\n    if let WantMaximized::UnsetBeforeInitial = want_maximized {\n        client.window(&surface).unset_maximized();\n    } else if let WantMaximized::BeforeInitial = want_maximized {\n        client.window(&surface).set_maximized();\n    }\n\n    if let WantFullscreen::UnsetBeforeInitial = want_fullscreen {\n        client.window(&surface).unset_fullscreen();\n    } else if let WantFullscreen::BeforeInitial(mon) = want_fullscreen {\n        let output = mon.map(|mon| client.output(&format!(\"headless-{mon}\")));\n        client.window(&surface).set_fullscreen(output.as_ref());\n    }\n\n    client.window(&surface).commit();\n    f.roundtrip(id);\n\n    let client = f.client(id);\n    let initial = client.window(&surface).format_recent_configures();\n\n    if let WantMaximized::UnsetAfterInitial = want_maximized {\n        client.window(&surface).unset_maximized();\n    } else if let WantMaximized::AfterInitial = want_maximized {\n        client.window(&surface).set_maximized();\n    }\n\n    if let WantFullscreen::UnsetAfterInitial = want_fullscreen {\n        client.window(&surface).unset_fullscreen();\n    } else if let WantFullscreen::AfterInitial(mon) = want_fullscreen {\n        let output = mon.map(|mon| client.output(&format!(\"headless-{mon}\")));\n        client.window(&surface).set_fullscreen(output.as_ref());\n    }\n\n    let window = client.window(&surface);\n    window.attach_new_buffer();\n    let serial = window.configures_received.last().unwrap().0;\n    window.ack_last_and_commit();\n    f.double_roundtrip(id);\n\n    // Commit to the post-initial configures.\n    let window = f.client(id).window(&surface);\n    let new_serial = window.configures_received.last().unwrap().0;\n    if new_serial != serial {\n        window.ack_last_and_commit();\n        f.double_roundtrip(id);\n    }\n\n    let window = f.client(id).window(&surface);\n    let post_map = window.format_recent_configures();\n\n    // If the window ended up fullscreen, unfullscreen it and output the configure.\n    let mut post_unfullscreen = String::new();\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    let is_fullscreen = mapped.sizing_mode().is_fullscreen();\n    let win = mapped.window.clone();\n    if is_fullscreen {\n        f.niri().layout.set_fullscreen(&win, false);\n        f.double_roundtrip(id);\n\n        let window = f.client(id).window(&surface);\n        window.ack_last_and_commit();\n        f.double_roundtrip(id);\n\n        let window = f.client(id).window(&surface);\n        post_unfullscreen = format!(\n            \"\\n\\nunfullscreen configure:\\n{}\",\n            window.format_recent_configures()\n        );\n    }\n\n    // If the window ended up maximized, unmaximize it and output the configure.\n    let mut post_unmaximize = String::new();\n    let mapped = f.niri().layout.windows().next().unwrap().1;\n    let is_maximized = mapped.sizing_mode().is_maximized();\n    let win = mapped.window.clone();\n    if is_maximized {\n        f.niri().layout.set_maximized(&win, false);\n        f.double_roundtrip(id);\n\n        let window = f.client(id).window(&surface);\n        window.ack_last_and_commit();\n        f.double_roundtrip(id);\n\n        let window = f.client(id).window(&surface);\n        post_unmaximize = format!(\n            \"\\n\\nunmaximize configure:\\n{}\",\n            window.format_recent_configures()\n        );\n    }\n\n    let snapshot = format!(\n        \"\\\ninitial configure:\n{initial}\n\npost-map configures:\n{post_map}{post_unfullscreen}{post_unmaximize}\",\n    );\n\n    let mut settings = insta::Settings::clone_current();\n    settings.set_snapshot_suffix(snapshot_suffix.join(\"-\"));\n    settings.set_description(snapshot_desc.join(\"\\n\"));\n    let _guard = settings.bind_to_scope();\n    assert_snapshot!(snapshot);\n}\n"
  },
  {
    "path": "src/ui/config_error_notification.rs",
    "content": "use std::cell::RefCell;\nuse std::collections::HashMap;\nuse std::path::{Path, PathBuf};\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse niri_config::Config;\nuse ordered_float::NotNan;\nuse pangocairo::cairo::{self, ImageSurface};\nuse pangocairo::pango::FontDescription;\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\nuse smithay::output::Output;\nuse smithay::reexports::gbm::Format as Fourcc;\nuse smithay::utils::{Point, Transform};\n\nuse crate::animation::{Animation, Clock};\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};\nuse crate::utils::{output_size, to_physical_precise_round};\n\nconst PADDING: i32 = 8;\nconst FONT: &str = \"sans 14px\";\nconst BORDER: i32 = 4;\n\npub struct ConfigErrorNotification {\n    state: State,\n    buffers: RefCell<HashMap<NotNan<f64>, Option<TextureBuffer<GlesTexture>>>>,\n\n    // If set, this is a \"Created config at {path}\" notification. If unset, this is a config error\n    // notification.\n    created_path: Option<PathBuf>,\n\n    clock: Clock,\n    config: Rc<RefCell<Config>>,\n}\n\nenum State {\n    Hidden,\n    Showing(Animation),\n    Shown(Duration),\n    Hiding(Animation),\n}\n\nimpl ConfigErrorNotification {\n    pub fn new(clock: Clock, config: Rc<RefCell<Config>>) -> Self {\n        Self {\n            state: State::Hidden,\n            buffers: RefCell::new(HashMap::new()),\n            created_path: None,\n            clock,\n            config,\n        }\n    }\n\n    fn animation(&self, from: f64, to: f64) -> Animation {\n        let c = self.config.borrow();\n        Animation::new(\n            self.clock.clone(),\n            from,\n            to,\n            0.,\n            c.animations.config_notification_open_close.0,\n        )\n    }\n\n    pub fn show_created(&mut self, created_path: &Path) {\n        if self.created_path.as_deref() != Some(created_path) {\n            self.created_path = Some(created_path.to_owned());\n            self.buffers.borrow_mut().clear();\n        }\n\n        self.state = State::Showing(self.animation(0., 1.));\n    }\n\n    pub fn show(&mut self) {\n        let c = self.config.borrow();\n        if c.config_notification.disable_failed {\n            return;\n        }\n\n        if self.created_path.is_some() {\n            self.created_path = None;\n            self.buffers.borrow_mut().clear();\n        }\n\n        // Show from scratch even if already showing to bring attention.\n        self.state = State::Showing(self.animation(0., 1.));\n    }\n\n    pub fn hide(&mut self) {\n        if matches!(self.state, State::Hidden) {\n            return;\n        }\n\n        self.state = State::Hiding(self.animation(1., 0.));\n    }\n\n    pub fn advance_animations(&mut self) {\n        match &mut self.state {\n            State::Hidden => (),\n            State::Showing(anim) => {\n                if anim.is_done() {\n                    let duration = if self.created_path.is_some() {\n                        // Make this quite a bit longer because it comes with a monitor modeset\n                        // (can take a while) and an important hotkeys popup diverting the\n                        // attention.\n                        Duration::from_secs(8)\n                    } else {\n                        Duration::from_secs(4)\n                    };\n                    self.state = State::Shown(self.clock.now_unadjusted() + duration);\n                }\n            }\n            State::Shown(deadline) => {\n                if self.clock.now_unadjusted() >= *deadline {\n                    self.hide();\n                }\n            }\n            State::Hiding(anim) => {\n                if anim.is_clamped_done() {\n                    self.state = State::Hidden;\n                }\n            }\n        }\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        !matches!(self.state, State::Hidden)\n    }\n\n    pub fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        output: &Output,\n    ) -> Option<PrimaryGpuTextureRenderElement> {\n        if matches!(self.state, State::Hidden) {\n            return None;\n        }\n\n        let scale = output.current_scale().fractional_scale();\n        let output_size = output_size(output);\n        let path = self.created_path.as_deref();\n\n        let mut buffers = self.buffers.borrow_mut();\n        let buffer = buffers\n            .entry(NotNan::new(scale).unwrap())\n            .or_insert_with(move || render(renderer.as_gles_renderer(), scale, path).ok());\n        let buffer = buffer.clone()?;\n\n        let size = buffer.logical_size();\n        let y_range = size.h + f64::from(PADDING) * 2.;\n\n        let x = (output_size.w - size.w).max(0.) / 2.;\n        let y = match &self.state {\n            State::Hidden => unreachable!(),\n            State::Showing(anim) | State::Hiding(anim) => -size.h + anim.value() * y_range,\n            State::Shown(_) => f64::from(PADDING) * 2.,\n        };\n\n        let location = Point::from((x, y));\n        let location = location.to_physical_precise_round(scale).to_logical(scale);\n\n        let elem = TextureRenderElement::from_texture_buffer(\n            buffer,\n            location,\n            1.,\n            None,\n            None,\n            Kind::Unspecified,\n        );\n        Some(PrimaryGpuTextureRenderElement(elem))\n    }\n}\n\nfn render(\n    renderer: &mut GlesRenderer,\n    scale: f64,\n    created_path: Option<&Path>,\n) -> anyhow::Result<TextureBuffer<GlesTexture>> {\n    let _span = tracy_client::span!(\"config_error_notification::render\");\n\n    let padding: i32 = to_physical_precise_round(scale, PADDING);\n\n    let mut text = error_text(true);\n    let mut border_color = (1., 0.3, 0.3);\n    if let Some(path) = created_path {\n        text = format!(\n            \"Created a default config file at \\\n             <span face='monospace' bgcolor='#000000'>{path:?}</span>\",\n        );\n        border_color = (0.5, 1., 0.5);\n    };\n\n    let mut font = FontDescription::from_string(FONT);\n    font.set_absolute_size(to_physical_precise_round(scale, font.size()));\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, 0, 0)?;\n    let cr = cairo::Context::new(&surface)?;\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_markup(&text);\n\n    let (mut width, mut height) = layout.pixel_size();\n    width += padding * 2;\n    height += padding * 2;\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;\n    let cr = cairo::Context::new(&surface)?;\n    cr.set_source_rgb(0.1, 0.1, 0.1);\n    cr.paint()?;\n\n    cr.move_to(padding.into(), padding.into());\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_markup(&text);\n\n    cr.set_source_rgb(1., 1., 1.);\n    pangocairo::functions::show_layout(&cr, &layout);\n\n    cr.move_to(0., 0.);\n    cr.line_to(width.into(), 0.);\n    cr.line_to(width.into(), height.into());\n    cr.line_to(0., height.into());\n    cr.line_to(0., 0.);\n    cr.set_source_rgb(border_color.0, border_color.1, border_color.2);\n    // Keep the border width even to avoid blurry edges.\n    cr.set_line_width((f64::from(BORDER) / 2. * scale).round() * 2.);\n    cr.stroke()?;\n    drop(cr);\n\n    let data = surface.take_data().unwrap();\n    let buffer = TextureBuffer::from_memory(\n        renderer,\n        &data,\n        Fourcc::Argb8888,\n        (width, height),\n        false,\n        scale,\n        Transform::Normal,\n        Vec::new(),\n    )?;\n\n    Ok(buffer)\n}\n\npub fn error_text(markup: bool) -> String {\n    let command = if markup {\n        \"<span face='monospace' bgcolor='#000000'>niri validate</span>\"\n    } else {\n        \"niri validate\"\n    };\n\n    format!(\"Failed to parse the config file. Please run {command} to see the errors.\")\n}\n"
  },
  {
    "path": "src/ui/exit_confirm_dialog.rs",
    "content": "use std::cell::RefCell;\nuse std::collections::HashMap;\nuse std::rc::Rc;\nuse std::sync::Mutex;\n\nuse niri_config::Config;\nuse ordered_float::NotNan;\nuse pangocairo::cairo::{self, ImageSurface};\nuse pangocairo::pango::{Alignment, FontDescription};\nuse smithay::backend::renderer::element::utils::RescaleRenderElement;\nuse smithay::backend::renderer::element::Kind;\nuse smithay::output::Output;\nuse smithay::reexports::gbm::Format as Fourcc;\nuse smithay::utils::{Point, Transform};\n\nuse crate::animation::{Animation, Clock};\nuse crate::niri_render_elements;\nuse crate::render_helpers::memory::MemoryBuffer;\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};\nuse crate::utils::{output_size, to_physical_precise_round};\n\nconst KEY_NAME: &str = \"Enter\";\nconst PADDING: i32 = 16;\nconst FONT: &str = \"sans 14px\";\nconst BORDER: i32 = 8;\nconst BACKDROP_COLOR: [f32; 4] = [0., 0., 0., 0.4];\n\npub struct ExitConfirmDialog {\n    state: State,\n    buffers: RefCell<HashMap<NotNan<f64>, Option<MemoryBuffer>>>,\n\n    clock: Clock,\n    config: Rc<RefCell<Config>>,\n}\n\nniri_render_elements! {\n    ExitConfirmDialogRenderElement => {\n        Texture = RescaleRenderElement<PrimaryGpuTextureRenderElement>,\n        SolidColor = SolidColorRenderElement,\n    }\n}\n\nstruct OutputData {\n    backdrop: SolidColorBuffer,\n}\n\nenum State {\n    Hidden,\n    Showing(Animation),\n    Visible,\n    Hiding(Animation),\n}\n\nimpl ExitConfirmDialog {\n    pub fn new(clock: Clock, config: Rc<RefCell<Config>>) -> Self {\n        let buffer = match render(1.) {\n            Ok(x) => Some(x),\n            Err(err) => {\n                warn!(\"error creating the exit confirm dialog: {err:?}\");\n                None\n            }\n        };\n\n        Self {\n            state: State::Hidden,\n            buffers: RefCell::new(HashMap::from([(NotNan::new(1.).unwrap(), buffer)])),\n            clock,\n            config,\n        }\n    }\n\n    pub fn can_show(&self) -> bool {\n        let buffers = self.buffers.borrow();\n        let fallback = &buffers[&NotNan::new(1.).unwrap()];\n        fallback.is_some()\n    }\n\n    fn animation(&self, from: f64, to: f64) -> Animation {\n        let c = self.config.borrow();\n        Animation::new(\n            self.clock.clone(),\n            from,\n            to,\n            0.,\n            c.animations.exit_confirmation_open_close.0,\n        )\n    }\n\n    fn value(&self) -> f64 {\n        match &self.state {\n            State::Hidden => 0.,\n            State::Showing(anim) | State::Hiding(anim) => anim.value(),\n            State::Visible => 1.,\n        }\n    }\n\n    /// Returns true if the dialog will be shown (even if it is already shown).\n    pub fn show(&mut self) -> bool {\n        if !self.can_show() {\n            return false;\n        }\n\n        if self.is_open() {\n            return true;\n        }\n\n        self.state = State::Showing(self.animation(self.value(), 1.));\n        true\n    }\n\n    /// Returns true if started the hide animation.\n    pub fn hide(&mut self) -> bool {\n        if !self.is_open() {\n            return false;\n        }\n\n        self.state = State::Hiding(self.animation(self.value(), 0.));\n        true\n    }\n\n    pub fn is_open(&self) -> bool {\n        matches!(self.state, State::Showing(_) | State::Visible)\n    }\n\n    pub fn advance_animations(&mut self) {\n        match &mut self.state {\n            State::Hidden => (),\n            State::Showing(anim) => {\n                if anim.is_done() {\n                    self.state = State::Visible;\n                }\n            }\n            State::Visible => (),\n            State::Hiding(anim) => {\n                if anim.is_clamped_done() {\n                    self.state = State::Hidden;\n                }\n            }\n        }\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        matches!(self.state, State::Showing(_) | State::Hiding(_))\n    }\n\n    pub fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        output: &Output,\n        push: &mut dyn FnMut(ExitConfirmDialogRenderElement),\n    ) {\n        let (value, clamped_value) = match &self.state {\n            State::Hidden => return,\n            State::Showing(anim) | State::Hiding(anim) => (anim.value(), anim.clamped_value()),\n            State::Visible => (1., 1.),\n        };\n        let _span = tracy_client::span!(\"ExitConfirmDialog::render\");\n\n        // Can be out of range when starting from past 0. or 1. from a spring bounce.\n        let clamped_value = clamped_value.clamp(0., 1.);\n\n        let scale = output.current_scale().fractional_scale();\n        let output_size = output_size(output);\n\n        let mut buffers = self.buffers.borrow_mut();\n        let Some(fallback) = buffers[&NotNan::new(1.).unwrap()].clone() else {\n            error!(\"exit confirm dialog opened without fallback buffer\");\n            return;\n        };\n\n        let buffer = buffers\n            .entry(NotNan::new(scale).unwrap())\n            .or_insert_with(|| render(scale).ok());\n        let buffer = buffer.as_ref().unwrap_or(&fallback);\n\n        let size = buffer.logical_size();\n        let Ok(buffer) = TextureBuffer::from_memory_buffer(renderer.as_gles_renderer(), buffer)\n        else {\n            return;\n        };\n\n        let location = (output_size.to_point() - size.to_point()).downscale(2.);\n        let mut location = location.to_physical_precise_round(scale).to_logical(scale);\n        location.x = f64::max(0., location.x);\n        location.y = f64::max(0., location.y);\n\n        let elem = TextureRenderElement::from_texture_buffer(\n            buffer,\n            location,\n            clamped_value as f32,\n            None,\n            None,\n            Kind::Unspecified,\n        );\n        let elem = PrimaryGpuTextureRenderElement(elem);\n        let elem = RescaleRenderElement::from_element(\n            elem,\n            (location + size.downscale(2.)).to_physical_precise_round(scale),\n            value.max(0.) * 0.2 + 0.8,\n        );\n        push(ExitConfirmDialogRenderElement::Texture(elem));\n\n        // Backdrop.\n        let data = output.user_data().get_or_insert(|| {\n            Mutex::new(OutputData {\n                backdrop: SolidColorBuffer::new(output_size, BACKDROP_COLOR),\n            })\n        });\n        let mut data = data.lock().unwrap();\n        data.backdrop.resize(output_size);\n\n        let elem = SolidColorRenderElement::from_buffer(\n            &data.backdrop,\n            Point::new(0., 0.),\n            clamped_value as f32,\n            Kind::Unspecified,\n        );\n        push(ExitConfirmDialogRenderElement::SolidColor(elem));\n    }\n}\n\nfn render(scale: f64) -> anyhow::Result<MemoryBuffer> {\n    let _span = tracy_client::span!(\"exit_confirm_dialog::render\");\n\n    let markup = text(true);\n\n    let padding: i32 = to_physical_precise_round(scale, PADDING);\n\n    let mut font = FontDescription::from_string(FONT);\n    font.set_absolute_size(to_physical_precise_round(scale, font.size()));\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, 0, 0)?;\n    let cr = cairo::Context::new(&surface)?;\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_alignment(Alignment::Center);\n    layout.set_markup(&markup);\n\n    let (mut width, mut height) = layout.pixel_size();\n    width += padding * 2;\n    height += padding * 2;\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;\n    let cr = cairo::Context::new(&surface)?;\n    cr.set_source_rgb(0.1, 0.1, 0.1);\n    cr.paint()?;\n\n    cr.move_to(padding.into(), padding.into());\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_alignment(Alignment::Center);\n    layout.set_markup(&markup);\n\n    cr.set_source_rgb(1., 1., 1.);\n    pangocairo::functions::show_layout(&cr, &layout);\n\n    cr.move_to(0., 0.);\n    cr.line_to(width.into(), 0.);\n    cr.line_to(width.into(), height.into());\n    cr.line_to(0., height.into());\n    cr.line_to(0., 0.);\n    cr.set_source_rgb(1., 0.3, 0.3);\n    // Keep the border width even to avoid blurry edges.\n    cr.set_line_width((f64::from(BORDER) / 2. * scale).round() * 2.);\n    cr.stroke()?;\n    drop(cr);\n\n    let data = surface.take_data().unwrap();\n    let buffer = MemoryBuffer::new(\n        data.to_vec(),\n        Fourcc::Argb8888,\n        (width, height),\n        scale,\n        Transform::Normal,\n    );\n\n    Ok(buffer)\n}\n\nfn text(markup: bool) -> String {\n    let key = if markup {\n        format!(\"<span face='mono' bgcolor='#2C2C2C'> {KEY_NAME} </span>\")\n    } else {\n        String::from(KEY_NAME)\n    };\n\n    format!(\n        \"Are you sure you want to exit niri?\\n\\n\\\n         Press {key} to confirm.\"\n    )\n}\n\n#[cfg(feature = \"dbus\")]\npub fn a11y_node() -> accesskit::Node {\n    let mut node = accesskit::Node::new(accesskit::Role::AlertDialog);\n    node.set_label(\"Exit niri\");\n    node.set_description(text(false));\n    node.set_modal();\n    node\n}\n"
  },
  {
    "path": "src/ui/hotkey_overlay.rs",
    "content": "use std::cell::RefCell;\nuse std::cmp::max;\nuse std::collections::HashMap;\nuse std::fmt::Write as _;\nuse std::iter::zip;\nuse std::rc::Rc;\n\nuse niri_config::{Action, Bind, Config, Key, ModKey, Modifiers, Trigger};\nuse pangocairo::cairo::{self, ImageSurface};\nuse pangocairo::pango::{AttrColor, AttrInt, AttrList, AttrString, FontDescription, Weight};\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\nuse smithay::input::keyboard::xkb::keysym_get_name;\nuse smithay::output::{Output, WeakOutput};\nuse smithay::reexports::gbm::Format as Fourcc;\nuse smithay::utils::{Scale, Transform};\n\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};\nuse crate::utils::{output_size, to_physical_precise_round};\n\nconst PADDING: i32 = 8;\n// const MARGIN: i32 = PADDING * 2;\nconst FONT: &str = \"sans 14px\";\nconst BORDER: i32 = 4;\nconst LINE_INTERVAL: i32 = 2;\nconst TITLE: &str = \"Important Hotkeys\";\n\npub struct HotkeyOverlay {\n    is_open: bool,\n    config: Rc<RefCell<Config>>,\n    mod_key: ModKey,\n    buffers: RefCell<HashMap<WeakOutput, RenderedOverlay>>,\n}\n\npub struct RenderedOverlay {\n    buffer: Option<TextureBuffer<GlesTexture>>,\n}\n\nimpl HotkeyOverlay {\n    pub fn new(config: Rc<RefCell<Config>>, mod_key: ModKey) -> Self {\n        Self {\n            is_open: false,\n            config,\n            mod_key,\n            buffers: RefCell::new(HashMap::new()),\n        }\n    }\n\n    pub fn show(&mut self) -> bool {\n        if !self.is_open {\n            self.is_open = true;\n            true\n        } else {\n            false\n        }\n    }\n\n    pub fn hide(&mut self) -> bool {\n        if self.is_open {\n            self.is_open = false;\n            true\n        } else {\n            false\n        }\n    }\n\n    pub fn is_open(&self) -> bool {\n        self.is_open\n    }\n\n    pub fn on_hotkey_config_updated(&mut self, mod_key: ModKey) {\n        self.mod_key = mod_key;\n        self.buffers.borrow_mut().clear();\n    }\n\n    pub fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        output: &Output,\n    ) -> Option<PrimaryGpuTextureRenderElement> {\n        if !self.is_open {\n            return None;\n        }\n\n        let scale = output.current_scale().fractional_scale();\n        let output_size = output_size(output);\n\n        let mut buffers = self.buffers.borrow_mut();\n        buffers.retain(|output, _| output.is_alive());\n\n        // FIXME: should probably use the working area rather than view size.\n        let weak = output.downgrade();\n        if let Some(rendered) = buffers.get(&weak) {\n            if let Some(buffer) = &rendered.buffer {\n                if buffer.texture_scale() != Scale::from(scale) {\n                    buffers.remove(&weak);\n                }\n            }\n        }\n\n        let rendered = buffers.entry(weak).or_insert_with(|| {\n            let renderer = renderer.as_gles_renderer();\n            render(renderer, &self.config.borrow(), self.mod_key, scale)\n                .unwrap_or_else(|_| RenderedOverlay { buffer: None })\n        });\n        let buffer = rendered.buffer.as_ref()?;\n\n        let size = buffer.logical_size();\n        let location = (output_size.to_f64().to_point() - size.to_point()).downscale(2.);\n        let mut location = location.to_physical_precise_round(scale).to_logical(scale);\n        location.x = f64::max(0., location.x);\n        location.y = f64::max(0., location.y);\n\n        let elem = TextureRenderElement::from_texture_buffer(\n            buffer.clone(),\n            location,\n            0.9,\n            None,\n            None,\n            Kind::Unspecified,\n        );\n\n        Some(PrimaryGpuTextureRenderElement(elem))\n    }\n\n    pub fn a11y_text(&self) -> String {\n        let config = self.config.borrow();\n        let actions = collect_actions(&config);\n\n        let mut buf = String::new();\n        writeln!(&mut buf, \"{TITLE}\").unwrap();\n\n        for action in actions {\n            let Some((key, action)) = format_bind(&config.binds.0, action) else {\n                continue;\n            };\n\n            let key = key.map(|key| key_name(true, self.mod_key, &key));\n            let key = key.as_deref().unwrap_or(\"not bound\");\n\n            let action = match pango::parse_markup(&action, '\\0') {\n                Ok((_attrs, text, _accel)) => text,\n                Err(_) => action.into(),\n            };\n\n            writeln!(&mut buf, \"{key} {action}\").unwrap();\n        }\n\n        buf\n    }\n}\n\nfn format_bind(binds: &[Bind], action: &Action) -> Option<(Option<Key>, String)> {\n    let mut bind_with_non_null = None;\n    let mut bind_with_custom_title = None;\n    let mut found_null_title = false;\n\n    for bind in binds {\n        if bind.action != *action {\n            continue;\n        }\n\n        match &bind.hotkey_overlay_title {\n            Some(Some(_)) => {\n                bind_with_custom_title.get_or_insert(bind);\n            }\n            Some(None) => {\n                found_null_title = true;\n            }\n            None => {\n                bind_with_non_null.get_or_insert(bind);\n            }\n        }\n    }\n\n    if bind_with_custom_title.is_none() && found_null_title {\n        return None;\n    }\n\n    let mut title = None;\n    let key = if let Some(bind) = bind_with_custom_title.or(bind_with_non_null) {\n        if let Some(Some(custom)) = &bind.hotkey_overlay_title {\n            title = Some(custom.clone());\n        }\n\n        Some(bind.key)\n    } else {\n        None\n    };\n    let title = title.unwrap_or_else(|| action_name(action));\n\n    Some((key, title))\n}\n\nfn collect_actions(config: &Config) -> Vec<&Action> {\n    let binds = &config.binds.0;\n\n    // Collect actions that we want to show.\n    let mut actions = vec![&Action::ShowHotkeyOverlay];\n\n    // Prefer Quit(false) if found, otherwise try Quit(true), and if there's neither, fall back to\n    // Quit(false).\n    if binds.iter().any(|bind| bind.action == Action::Quit(false)) {\n        actions.push(&Action::Quit(false));\n    } else if binds.iter().any(|bind| bind.action == Action::Quit(true)) {\n        actions.push(&Action::Quit(true));\n    } else {\n        actions.push(&Action::Quit(false));\n    }\n\n    actions.extend(&[\n        &Action::CloseWindow,\n        &Action::FocusColumnLeft,\n        &Action::FocusColumnRight,\n        &Action::MoveColumnLeft,\n        &Action::MoveColumnRight,\n        &Action::FocusWorkspaceDown,\n        &Action::FocusWorkspaceUp,\n    ]);\n\n    // Prefer move-column-to-workspace-down, but fall back to move-window-to-workspace-down.\n    if let Some(bind) = binds\n        .iter()\n        .find(|bind| matches!(bind.action, Action::MoveColumnToWorkspaceDown(_)))\n    {\n        actions.push(&bind.action);\n    } else if binds\n        .iter()\n        .any(|bind| matches!(bind.action, Action::MoveWindowToWorkspaceDown(_)))\n    {\n        actions.push(&Action::MoveWindowToWorkspaceDown(true));\n    } else {\n        actions.push(&Action::MoveColumnToWorkspaceDown(true));\n    }\n\n    // Same for -up.\n    if let Some(bind) = binds\n        .iter()\n        .find(|bind| matches!(bind.action, Action::MoveColumnToWorkspaceUp(_)))\n    {\n        actions.push(&bind.action);\n    } else if binds\n        .iter()\n        .any(|bind| matches!(bind.action, Action::MoveWindowToWorkspaceUp(_)))\n    {\n        actions.push(&Action::MoveWindowToWorkspaceUp(true));\n    } else {\n        actions.push(&Action::MoveColumnToWorkspaceUp(true));\n    }\n\n    actions.extend(&[\n        &Action::SwitchPresetColumnWidth,\n        &Action::MaximizeColumn,\n        &Action::ConsumeOrExpelWindowLeft,\n        &Action::ConsumeOrExpelWindowRight,\n        &Action::ToggleWindowFloating,\n        &Action::SwitchFocusBetweenFloatingAndTiling,\n        &Action::ToggleOverview,\n    ]);\n\n    // Screenshot is not as important, can omit if not bound.\n    if let Some(bind) = binds\n        .iter()\n        .find(|bind| matches!(bind.action, Action::Screenshot(_, _)))\n    {\n        actions.push(&bind.action);\n    }\n\n    // Add actions with a custom hotkey-overlay-title.\n    for bind in binds {\n        if matches!(bind.hotkey_overlay_title, Some(Some(_))) {\n            // Avoid duplicate actions.\n            if !actions.contains(&&bind.action) {\n                actions.push(&bind.action);\n            }\n        }\n    }\n\n    // Add the spawn actions.\n    for bind in binds.iter().filter(|bind| {\n        matches!(bind.action, Action::Spawn(_) | Action::SpawnSh(_))\n            // Only show binds with Mod or Super to filter out stuff like volume up/down.\n            && (bind.key.modifiers.contains(Modifiers::COMPOSITOR)\n                || bind.key.modifiers.contains(Modifiers::SUPER))\n            // Also filter out wheel and touchpad scroll binds.\n            && matches!(bind.key.trigger, Trigger::Keysym(_))\n    }) {\n        let action = &bind.action;\n\n        // We only show one bind for each action, so we need to deduplicate the Spawn actions.\n        if !actions.contains(&action) {\n            actions.push(action);\n        }\n    }\n\n    if config.hotkey_overlay.hide_not_bound {\n        // Only keep actions that have been bound\n        actions.retain(|&action| binds.iter().any(|bind| bind.action == *action))\n    }\n\n    actions\n}\n\nfn render(\n    renderer: &mut GlesRenderer,\n    config: &Config,\n    mod_key: ModKey,\n    scale: f64,\n) -> anyhow::Result<RenderedOverlay> {\n    let _span = tracy_client::span!(\"hotkey_overlay::render\");\n\n    // let margin = MARGIN * scale;\n    let padding: i32 = to_physical_precise_round(scale, PADDING);\n    let line_interval: i32 = to_physical_precise_round(scale, LINE_INTERVAL);\n\n    // FIXME: if it doesn't fit, try splitting in two columns or something.\n    // let mut target_size = output_size;\n    // target_size.w -= margin * 2;\n    // target_size.h -= margin * 2;\n    // anyhow::ensure!(target_size.w > 0 && target_size.h > 0);\n\n    let strings = collect_actions(config)\n        .into_iter()\n        .filter_map(|action| format_bind(&config.binds.0, action))\n        .map(|(key, action)| {\n            let key = key.map(|key| key_name(false, mod_key, &key));\n            let key = key.as_deref().unwrap_or(\"(not bound)\");\n            let key = format!(\" {key} \");\n            (key, action)\n        })\n        .collect::<Vec<_>>();\n\n    let mut font = FontDescription::from_string(FONT);\n    font.set_absolute_size(to_physical_precise_round(scale, font.size()));\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, 0, 0)?;\n    let cr = cairo::Context::new(&surface)?;\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n\n    let bold = AttrList::new();\n    bold.insert(AttrInt::new_weight(Weight::Bold));\n    layout.set_attributes(Some(&bold));\n    layout.set_text(TITLE);\n    let title_size = layout.pixel_size();\n\n    let attrs = AttrList::new();\n    attrs.insert(AttrString::new_family(\"Monospace\"));\n    attrs.insert(AttrColor::new_background(12000, 12000, 12000));\n\n    layout.set_attributes(Some(&attrs));\n    let key_sizes = strings\n        .iter()\n        .map(|(key, _)| {\n            layout.set_text(key);\n            layout.pixel_size()\n        })\n        .collect::<Vec<_>>();\n\n    layout.set_attributes(None);\n    let action_sizes = strings\n        .iter()\n        .map(|(_, action)| {\n            layout.set_markup(action);\n            layout.pixel_size()\n        })\n        .collect::<Vec<_>>();\n\n    let key_width = key_sizes.iter().map(|(w, _)| w).max().unwrap();\n    let action_width = action_sizes.iter().map(|(w, _)| w).max().unwrap();\n    let mut width = key_width + padding + action_width;\n\n    let mut height = zip(&key_sizes, &action_sizes)\n        .map(|((_, key_h), (_, act_h))| max(key_h, act_h))\n        .sum::<i32>()\n        + (key_sizes.len() - 1) as i32 * line_interval\n        + title_size.1\n        + padding;\n\n    width += padding * 2;\n    height += padding * 2;\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;\n    let cr = cairo::Context::new(&surface)?;\n    cr.set_source_rgb(0.1, 0.1, 0.1);\n    cr.paint()?;\n\n    cr.move_to(padding.into(), padding.into());\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n\n    cr.set_source_rgb(1., 1., 1.);\n\n    cr.move_to(((width - title_size.0) / 2).into(), padding.into());\n    layout.set_attributes(Some(&bold));\n    layout.set_text(TITLE);\n    pangocairo::functions::show_layout(&cr, &layout);\n\n    cr.move_to(padding.into(), (padding + title_size.1 + padding).into());\n\n    for ((key, action), ((_, key_h), (_, act_h))) in zip(&strings, zip(&key_sizes, &action_sizes)) {\n        layout.set_attributes(Some(&attrs));\n        layout.set_text(key);\n        pangocairo::functions::show_layout(&cr, &layout);\n\n        cr.rel_move_to((key_width + padding).into(), 0.);\n\n        let (attrs, text) = match pango::parse_markup(action, '\\0') {\n            Ok((attrs, text, _accel)) => (Some(attrs), text),\n            Err(err) => {\n                warn!(\"error parsing markup for key {key}: {err}\");\n                (None, action.into())\n            }\n        };\n\n        layout.set_attributes(attrs.as_ref());\n        layout.set_text(&text);\n        pangocairo::functions::show_layout(&cr, &layout);\n\n        cr.rel_move_to(\n            (-(key_width + padding)).into(),\n            (max(key_h, act_h) + line_interval).into(),\n        );\n    }\n\n    cr.move_to(0., 0.);\n    cr.line_to(width.into(), 0.);\n    cr.line_to(width.into(), height.into());\n    cr.line_to(0., height.into());\n    cr.line_to(0., 0.);\n    cr.set_source_rgb(0.5, 0.8, 1.0);\n    // Keep the border width even to avoid blurry edges.\n    cr.set_line_width((f64::from(BORDER) / 2. * scale).round() * 2.);\n    cr.stroke()?;\n    drop(cr);\n\n    let data = surface.take_data().unwrap();\n    let buffer = TextureBuffer::from_memory(\n        renderer,\n        &data,\n        Fourcc::Argb8888,\n        (width, height),\n        false,\n        scale,\n        Transform::Normal,\n        Vec::new(),\n    )?;\n\n    Ok(RenderedOverlay {\n        buffer: Some(buffer),\n    })\n}\n\nfn action_name(action: &Action) -> String {\n    match action {\n        Action::Quit(_) => String::from(\"Exit niri\"),\n        Action::ShowHotkeyOverlay => String::from(\"Show Important Hotkeys\"),\n        Action::CloseWindow => String::from(\"Close Focused Window\"),\n        Action::FocusColumnLeft => String::from(\"Focus Column to the Left\"),\n        Action::FocusColumnRight => String::from(\"Focus Column to the Right\"),\n        Action::MoveColumnLeft => String::from(\"Move Column Left\"),\n        Action::MoveColumnRight => String::from(\"Move Column Right\"),\n        Action::FocusWorkspaceDown => String::from(\"Switch Workspace Down\"),\n        Action::FocusWorkspaceUp => String::from(\"Switch Workspace Up\"),\n        Action::MoveColumnToWorkspaceDown(_) => String::from(\"Move Column to Workspace Down\"),\n        Action::MoveColumnToWorkspaceUp(_) => String::from(\"Move Column to Workspace Up\"),\n        Action::MoveWindowToWorkspaceDown(_) => String::from(\"Move Window to Workspace Down\"),\n        Action::MoveWindowToWorkspaceUp(_) => String::from(\"Move Window to Workspace Up\"),\n        Action::SwitchPresetColumnWidth => String::from(\"Switch Preset Column Widths\"),\n        Action::MaximizeColumn => String::from(\"Maximize Column\"),\n        Action::ConsumeOrExpelWindowLeft => String::from(\"Consume or Expel Window Left\"),\n        Action::ConsumeOrExpelWindowRight => String::from(\"Consume or Expel Window Right\"),\n        Action::ToggleWindowFloating => String::from(\"Move Window Between Floating and Tiling\"),\n        Action::SwitchFocusBetweenFloatingAndTiling => {\n            String::from(\"Switch Focus Between Floating and Tiling\")\n        }\n        Action::ToggleOverview => String::from(\"Open the Overview\"),\n        Action::Screenshot(_, _) => String::from(\"Take a Screenshot\"),\n        Action::Spawn(args) => format!(\n            \"Spawn <span face='monospace' bgcolor='#000000'>{}</span>\",\n            args.first().unwrap_or(&String::new())\n        ),\n        Action::SpawnSh(command) => format!(\n            \"Spawn <span face='monospace' bgcolor='#000000'>{}</span>\",\n            // Fairly crude but should get the job done in most cases.\n            command.split_ascii_whitespace().next().unwrap_or(\"\")\n        ),\n        _ => String::from(\"FIXME: Unknown\"),\n    }\n}\n\nfn key_name(screen_reader: bool, mod_key: ModKey, key: &Key) -> String {\n    let mut name = String::new();\n\n    let has_comp_mod = key.modifiers.contains(Modifiers::COMPOSITOR);\n\n    // Compositor mod goes first.\n    if has_comp_mod {\n        match mod_key {\n            ModKey::Super => {\n                name.push_str(\"Super + \");\n            }\n            ModKey::Alt => {\n                name.push_str(\"Alt + \");\n            }\n            ModKey::Shift => {\n                name.push_str(\"Shift + \");\n            }\n            ModKey::Ctrl => {\n                name.push_str(\"Ctrl + \");\n            }\n            ModKey::IsoLevel3Shift => {\n                name.push_str(\"Mod5 + \");\n            }\n            ModKey::IsoLevel5Shift => {\n                name.push_str(\"Mod3 + \");\n            }\n        }\n    }\n\n    if key.modifiers.contains(Modifiers::SUPER) && !(has_comp_mod && mod_key == ModKey::Super) {\n        name.push_str(\"Super + \");\n    }\n    if key.modifiers.contains(Modifiers::CTRL) && !(has_comp_mod && mod_key == ModKey::Ctrl) {\n        name.push_str(\"Ctrl + \");\n    }\n    if key.modifiers.contains(Modifiers::SHIFT) && !(has_comp_mod && mod_key == ModKey::Shift) {\n        name.push_str(\"Shift + \");\n    }\n    if key.modifiers.contains(Modifiers::ALT) && !(has_comp_mod && mod_key == ModKey::Alt) {\n        name.push_str(\"Alt + \");\n    }\n    if key.modifiers.contains(Modifiers::ISO_LEVEL3_SHIFT)\n        && !(has_comp_mod && mod_key == ModKey::IsoLevel3Shift)\n    {\n        name.push_str(\"Mod5 + \");\n    }\n    if key.modifiers.contains(Modifiers::ISO_LEVEL5_SHIFT)\n        && !(has_comp_mod && mod_key == ModKey::IsoLevel5Shift)\n    {\n        name.push_str(\"Mod3 + \");\n    }\n\n    let pretty = match key.trigger {\n        Trigger::Keysym(keysym) => prettify_keysym_name(screen_reader, &keysym_get_name(keysym)),\n        Trigger::MouseLeft => String::from(\"Mouse Left\"),\n        Trigger::MouseRight => String::from(\"Mouse Right\"),\n        Trigger::MouseMiddle => String::from(\"Mouse Middle\"),\n        Trigger::MouseBack => String::from(\"Mouse Back\"),\n        Trigger::MouseForward => String::from(\"Mouse Forward\"),\n        Trigger::WheelScrollDown => String::from(\"Wheel Scroll Down\"),\n        Trigger::WheelScrollUp => String::from(\"Wheel Scroll Up\"),\n        Trigger::WheelScrollLeft => String::from(\"Wheel Scroll Left\"),\n        Trigger::WheelScrollRight => String::from(\"Wheel Scroll Right\"),\n        Trigger::TouchpadScrollDown => String::from(\"Touchpad Scroll Down\"),\n        Trigger::TouchpadScrollUp => String::from(\"Touchpad Scroll Up\"),\n        Trigger::TouchpadScrollLeft => String::from(\"Touchpad Scroll Left\"),\n        Trigger::TouchpadScrollRight => String::from(\"Touchpad Scroll Right\"),\n    };\n    name.push_str(&pretty);\n\n    name\n}\n\nfn prettify_keysym_name(screen_reader: bool, name: &str) -> String {\n    let name = if screen_reader {\n        name\n    } else {\n        match name {\n            \"slash\" => \"/\",\n            \"comma\" => \",\",\n            \"period\" => \".\",\n            \"minus\" => \"-\",\n            \"equal\" => \"=\",\n            \"grave\" => \"`\",\n            \"bracketleft\" => \"[\",\n            \"bracketright\" => \"]\",\n            _ => name,\n        }\n    };\n\n    let name = match name {\n        \"Next\" => \"Page Down\",\n        \"Prior\" => \"Page Up\",\n        \"Print\" => \"PrtSc\",\n        \"Return\" => \"Enter\",\n        \"space\" => \"Space\",\n        _ => name,\n    };\n\n    if name.len() == 1 && name.is_ascii() {\n        name.to_ascii_uppercase()\n    } else {\n        name.into()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::assert_snapshot;\n\n    use super::*;\n\n    #[track_caller]\n    fn check(config: &str, action: Action) -> String {\n        let config = Config::parse_mem(config).unwrap();\n        if let Some((key, title)) = format_bind(&config.binds.0, &action) {\n            let key = key.map(|key| key_name(false, ModKey::Super, &key));\n            let key = key.as_deref().unwrap_or(\"(not bound)\");\n            format!(\" {key} : {title}\")\n        } else {\n            String::from(\"None\")\n        }\n    }\n\n    #[test]\n    fn test_format_bind() {\n        // Not bound.\n        assert_snapshot!(check(\"\", Action::Screenshot(true, None)), @\" (not bound) : Take a Screenshot\");\n\n        // Bound with a default title.\n        assert_snapshot!(\n            check(\n                r#\"binds {\n                    Mod+P { screenshot; }\n                }\"#,\n                Action::Screenshot(true, None),\n            ),\n            @\" Super + P : Take a Screenshot\"\n        );\n\n        // Custom title.\n        assert_snapshot!(\n            check(\n                r#\"binds {\n                    Mod+P hotkey-overlay-title=\"Hello\" { screenshot; }\n                }\"#,\n                Action::Screenshot(true, None),\n            ),\n            @\" Super + P : Hello\"\n        );\n\n        // Prefer first bind.\n        assert_snapshot!(\n            check(\n                r#\"binds {\n                    Mod+P { screenshot; }\n                    Print { screenshot; }\n                }\"#,\n                Action::Screenshot(true, None),\n            ),\n            @\" Super + P : Take a Screenshot\"\n        );\n\n        // Prefer bind with custom title.\n        assert_snapshot!(\n            check(\n                r#\"binds {\n                    Mod+P { screenshot; }\n                    Print hotkey-overlay-title=\"My Cool Bind\" { screenshot; }\n                }\"#,\n                Action::Screenshot(true, None),\n            ),\n            @\" PrtSc : My Cool Bind\"\n        );\n\n        // Prefer first bind with custom title.\n        assert_snapshot!(\n            check(\n                r#\"binds {\n                    Mod+P hotkey-overlay-title=\"First\" { screenshot; }\n                    Print hotkey-overlay-title=\"My Cool Bind\" { screenshot; }\n                }\"#,\n                Action::Screenshot(true, None),\n            ),\n            @\" Super + P : First\"\n        );\n\n        // Any bind with null title hides it.\n        assert_snapshot!(\n            check(\n                r#\"binds {\n                    Mod+P { screenshot; }\n                    Print hotkey-overlay-title=null { screenshot; }\n                }\"#,\n                Action::Screenshot(true, None),\n            ),\n            @\"None\"\n        );\n\n        // Custom title takes preference over null.\n        assert_snapshot!(\n            check(\n                r#\"binds {\n                    Mod+P hotkey-overlay-title=\"Hello\" { screenshot; }\n                    Print hotkey-overlay-title=null { screenshot; }\n                }\"#,\n                Action::Screenshot(true, None),\n            ),\n            @\" Super + P : Hello\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/ui/mod.rs",
    "content": "pub mod config_error_notification;\npub mod exit_confirm_dialog;\npub mod hotkey_overlay;\npub mod mru;\npub mod screen_transition;\npub mod screenshot_ui;\n"
  },
  {
    "path": "src/ui/mru/tests.rs",
    "content": "use proptest::prelude::*;\nuse proptest_derive::Arbitrary;\n\nuse super::*;\n\nfn create_thumbnail() -> Thumbnail {\n    Thumbnail {\n        id: MappedId::next(),\n        timestamp: None,\n        on_current_output: false,\n        on_current_workspace: false,\n        app_id: None,\n        size: Size::new(100, 100),\n        clock: Clock::with_time(Duration::ZERO),\n        config: niri_config::MruPreviews::default(),\n        open_animation: None,\n        move_animation: None,\n        title_texture: Default::default(),\n        background: RefCell::new(FocusRing::new(Default::default())),\n        border: RefCell::new(FocusRing::new(Default::default())),\n    }\n}\n\n#[test]\nfn remove_last_window_out_of_two() {\n    let ops = [Op::Backward, Op::Remove(1)];\n\n    let thumbnails = vec![create_thumbnail(), create_thumbnail()];\n    let current_id = thumbnails.first().map(|t| t.id);\n    let mut mru = WindowMru {\n        thumbnails,\n        current_id,\n        scope: MruScope::All,\n        app_id_filter: None,\n    };\n\n    check_ops(&mut mru, &ops);\n}\n\nfn arbitrary_scope() -> impl Strategy<Value = MruScope> {\n    prop_oneof![\n        Just(MruScope::All),\n        Just(MruScope::Output),\n        Just(MruScope::Workspace),\n    ]\n}\n\nfn arbitrary_filter() -> impl Strategy<Value = MruFilter> {\n    prop_oneof![Just(MruFilter::All), Just(MruFilter::AppId)]\n}\n\nfn arbitrary_app_id() -> impl Strategy<Value = Option<String>> {\n    prop_oneof![Just(None), Just(Some(1)), Just(Some(2))]\n        .prop_map(|id| id.map(|id| format!(\"app-{id}\")))\n}\n\nprop_compose! {\n    fn arbitrary_thumbnail()(\n        timestamp: Option<Duration>,\n        on_current_output: bool,\n        on_current_workspace: bool,\n        app_id in arbitrary_app_id(),\n    ) -> Thumbnail {\n        let mut thumbnail = create_thumbnail();\n        thumbnail.timestamp = timestamp;\n        thumbnail.on_current_workspace = on_current_workspace;\n        thumbnail.on_current_output = on_current_output;\n        thumbnail.app_id = app_id;\n        thumbnail\n    }\n}\n\nprop_compose! {\n    fn arbitrary_mru()(\n        thumbnails in proptest::collection::vec(arbitrary_thumbnail(), 1..10),\n    ) -> WindowMru {\n        let current_id = thumbnails.first().map(|t| t.id);\n        WindowMru {\n            thumbnails,\n            current_id,\n            scope: MruScope::All,\n            app_id_filter: None,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Arbitrary)]\nenum Op {\n    Forward,\n    Backward,\n    First,\n    Last,\n    SetScope(#[proptest(strategy = \"arbitrary_scope()\")] MruScope),\n    SetFilter(#[proptest(strategy = \"arbitrary_filter()\")] MruFilter),\n    Remove(#[proptest(strategy = \"1..10usize\")] usize),\n}\n\nimpl Op {\n    fn apply(&self, mru: &mut WindowMru) {\n        match self {\n            Op::Forward => mru.forward(),\n            Op::Backward => mru.backward(),\n            Op::First => mru.first(),\n            Op::Last => mru.last(),\n            Op::SetScope(scope) => {\n                mru.set_scope(*scope);\n            }\n            Op::SetFilter(filter) => {\n                mru.set_filter(*filter);\n            }\n            Op::Remove(idx) => {\n                if *idx < mru.thumbnails.len() {\n                    mru.remove_by_idx(*idx);\n                }\n            }\n        }\n    }\n}\n\nfn check_ops(mru: &mut WindowMru, ops: &[Op]) {\n    for op in ops {\n        op.apply(mru);\n        mru.verify_invariants();\n    }\n}\n\nproptest! {\n    #[test]\n    fn random_operations_dont_panic(\n        mut mru in arbitrary_mru(),\n        ops: Vec<Op>,\n    ) {\n        check_ops(&mut mru, &ops);\n    }\n}\n"
  },
  {
    "path": "src/ui/mru.rs",
    "content": "use std::cell::RefCell;\nuse std::cmp::min;\nuse std::collections::HashMap;\nuse std::mem;\nuse std::rc::Rc;\nuse std::time::Duration;\n\nuse anyhow::ensure;\nuse niri_config::{\n    Action, Bind, Color, Config, CornerRadius, GradientInterpolation, Key, Modifiers, MruDirection,\n    MruFilter, MruScope, Trigger,\n};\nuse pango::FontDescription;\nuse pangocairo::cairo::{self, ImageSurface};\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::renderer::element::utils::{\n    Relocate, RelocateRenderElement, RescaleRenderElement,\n};\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\nuse smithay::backend::renderer::Color32F;\nuse smithay::input::keyboard::Keysym;\nuse smithay::output::Output;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Size, Transform};\n\nuse crate::animation::{Animation, Clock};\nuse crate::layout::focus_ring::{FocusRing, FocusRingRenderElement};\nuse crate::layout::{Layout, LayoutElement as _, LayoutElementRenderElement};\nuse crate::niri::Niri;\nuse crate::niri_render_elements;\nuse crate::render_helpers::border::BorderRenderElement;\nuse crate::render_helpers::clipped_surface::ClippedSurfaceRenderElement;\nuse crate::render_helpers::gradient_fade_texture::GradientFadeTextureRenderElement;\nuse crate::render_helpers::offscreen::{OffscreenBuffer, OffscreenRenderElement};\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};\nuse crate::render_helpers::RenderTarget;\nuse crate::utils::{\n    baba_is_float_offset, output_size, round_logical_in_physical, to_physical_precise_round,\n    with_toplevel_role,\n};\nuse crate::window::mapped::MappedId;\nuse crate::window::Mapped;\n\n#[cfg(test)]\nmod tests;\n\n/// Windows up to this size don't get scaled further down.\nconst PREVIEW_MIN_SIZE: f64 = 16.;\n\n/// Border width on the selected window preview.\nconst BORDER: f64 = 2.;\n\n/// Gap from the window preview to the window title.\nconst TITLE_GAP: f64 = 14.;\n\n/// Gap between thumbnails.\nconst GAP: f64 = 16.;\n\n/// How much of the next window will always peek from the side of the screen.\nconst STRUT: f64 = 192.;\n\n/// Padding in the scope indication panel.\nconst PANEL_PADDING: i32 = 12;\n\n/// Border size of the scope indication panel.\nconst PANEL_BORDER: i32 = 4;\n\n/// Backdrop color behind the previews.\nconst BACKDROP_COLOR: Color32F = Color32F::new(0., 0., 0., 0.8);\n\n/// Font used to render the window titles.\nconst FONT: &str = \"sans 14px\";\n\n/// Scopes in the order they are cycled through.\n///\n/// Count must match one defined in `generate_scope_panels()`.\nstatic SCOPE_CYCLE: [MruScope; 3] = [MruScope::All, MruScope::Workspace, MruScope::Output];\n\n/// Window MRU traversal context.\n#[derive(Debug)]\npub struct WindowMru {\n    /// Windows in MRU order.\n    thumbnails: Vec<Thumbnail>,\n\n    /// Id of the currently selected window.\n    current_id: Option<MappedId>,\n\n    /// Current scope.\n    scope: MruScope,\n\n    /// Current filter.\n    app_id_filter: Option<String>,\n}\n\npub struct WindowMruUi {\n    state: UiState,\n    preset_opened_binds: Vec<Bind>,\n    dynamic_opened_binds: Vec<Bind>,\n    config: Rc<RefCell<Config>>,\n}\n\npub enum MruCloseRequest {\n    Cancel,\n    Confirm,\n}\n\nniri_render_elements! {\n    ThumbnailRenderElement<R> => {\n        LayoutElement = LayoutElementRenderElement<R>,\n        ClippedSurface = ClippedSurfaceRenderElement<R>,\n        Border = BorderRenderElement,\n    }\n}\n\nniri_render_elements! {\n    WindowMruUiRenderElement<R> => {\n        SolidColor = SolidColorRenderElement,\n        TextureElement = PrimaryGpuTextureRenderElement,\n        GradientFadeElem = GradientFadeTextureRenderElement,\n        FocusRing = FocusRingRenderElement,\n        Offscreen = OffscreenRenderElement,\n        Thumbnail = RelocateRenderElement<RescaleRenderElement<ThumbnailRenderElement<R>>>,\n    }\n}\n\nenum UiState {\n    Open(Inner),\n    Closing {\n        inner: Inner,\n        anim: Animation,\n    },\n    Closed {\n        /// Scope used when the UI was last opened.\n        previous_scope: MruScope,\n    },\n}\n\n/// State of an opened MRU UI.\nstruct Inner {\n    /// List of Window Ids to display in the MRU UI.\n    wmru: WindowMru,\n\n    /// View position relative to the leftmost visible window.\n    view_pos: ViewPos,\n\n    // If true, don't automatically move the current thumbnail in-view. Set on pointer motion.\n    freeze_view: bool,\n\n    /// Animation clock.\n    clock: Clock,\n\n    /// Current config.\n    config: Rc<RefCell<Config>>,\n\n    /// Time when the UI should appear.\n    open_at: Duration,\n\n    /// Output the UI was opened on.\n    output: Output,\n\n    /// Scope panel textures.\n    scope_panel: RefCell<ScopePanel>,\n\n    /// Backdrop buffers for each output.\n    backdrop_buffers: RefCell<HashMap<Output, SolidColorBuffer>>,\n\n    /// Offscreen buffer for the closing fade animation on the main output.\n    offscreen: OffscreenBuffer,\n}\n\n#[derive(Debug)]\nenum ViewPos {\n    /// The view position is static.\n    Static(f64),\n    /// The view position is animating.\n    Animation(Animation),\n}\n\n#[derive(Debug)]\nstruct MoveAnimation {\n    anim: Animation,\n    from: f64,\n}\n\ntype MruTexture = TextureBuffer<GlesTexture>;\n\n/// Cached title texture.\n#[derive(Debug, Default)]\nstruct TitleTexture {\n    title: String,\n    scale: f64,\n    texture: Option<Option<MruTexture>>,\n}\n\n/// Cached scope panel textures.\n#[derive(Debug, Default)]\nstruct ScopePanel {\n    scale: f64,\n    textures: Option<Option<[MruTexture; 3]>>,\n}\n\n#[derive(Debug)]\nstruct Thumbnail {\n    id: MappedId,\n\n    /// Focus timestamp, if any.\n    timestamp: Option<Duration>,\n    /// Whether the window is on the current MRU workspace.\n    on_current_workspace: bool,\n    /// Whether the window is on the current MRU output.\n    on_current_output: bool,\n\n    /// Cached app ID of the window.\n    ///\n    /// Currently not updated live to avoid having to refilter windows.\n    app_id: Option<String>,\n    /// Cached size of the window.\n    size: Size<i32, Logical>,\n\n    clock: Clock,\n    config: niri_config::MruPreviews,\n    open_animation: Option<Animation>,\n    move_animation: Option<MoveAnimation>,\n    title_texture: RefCell<TitleTexture>,\n    background: RefCell<FocusRing>,\n    border: RefCell<FocusRing>,\n}\n\nimpl Thumbnail {\n    fn from_mapped(mapped: &Mapped, clock: Clock, config: niri_config::MruPreviews) -> Self {\n        let app_id = with_toplevel_role(mapped.toplevel(), |role| role.app_id.clone());\n\n        let background = FocusRing::new(niri_config::FocusRing {\n            off: false,\n            width: 0.,\n            active_gradient: None,\n            ..Default::default()\n        });\n        let border = FocusRing::new(niri_config::FocusRing {\n            off: false,\n            active_gradient: None,\n            ..Default::default()\n        });\n\n        Self {\n            id: mapped.id(),\n            timestamp: mapped.get_focus_timestamp(),\n            on_current_output: false,\n            on_current_workspace: false,\n            app_id,\n            size: mapped.size(),\n            clock,\n            config,\n            open_animation: None,\n            move_animation: None,\n            title_texture: Default::default(),\n            background: RefCell::new(background),\n            border: RefCell::new(border),\n        }\n    }\n\n    fn are_animations_ongoing(&self) -> bool {\n        self.open_animation.is_some() || self.move_animation.is_some()\n    }\n\n    fn advance_animations(&mut self) {\n        self.open_animation.take_if(|a| a.is_done());\n        self.move_animation.take_if(|a| a.anim.is_done());\n    }\n\n    /// Animate thumbnail motion from given location.\n    fn animate_move_from_with_config(&mut self, from: f64, config: niri_config::Animation) {\n        let current_offset = self.render_offset();\n\n        // Preserve the previous config if ongoing.\n        let anim = self.move_animation.take().map(|ma| ma.anim);\n        let anim = anim\n            .map(|anim| anim.restarted(1., 0., 0.))\n            .unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config));\n\n        self.move_animation = Some(MoveAnimation {\n            anim,\n            from: from + current_offset,\n        });\n    }\n\n    fn animate_open_with_config(&mut self, config: niri_config::Animation) {\n        self.open_animation = Some(Animation::new(self.clock.clone(), 0., 1., 0., config));\n    }\n\n    fn render_offset(&self) -> f64 {\n        self.move_animation\n            .as_ref()\n            .map(|ma| ma.from * ma.anim.value())\n            .unwrap_or_default()\n    }\n\n    fn update_window(&mut self, mapped: &Mapped) {\n        self.size = mapped.size();\n    }\n\n    fn preview_size(&self, output_size: Size<f64, Logical>, scale: f64) -> Size<f64, Logical> {\n        let max_height = f64::max(1., self.config.max_height);\n        let max_scale = f64::max(0.001, self.config.max_scale);\n\n        let max_height = f64::min(max_height, output_size.h * max_scale);\n        let output_ratio = output_size.w / output_size.h;\n        let max_width = max_height * output_ratio;\n\n        let size = self.size.to_f64();\n        let min_scale = f64::min(1., PREVIEW_MIN_SIZE / f64::max(size.w, size.h));\n\n        let thumb_scale = f64::min(max_width / size.w, max_height / size.h);\n        let thumb_scale = f64::min(max_scale, thumb_scale);\n        let thumb_scale = f64::max(min_scale, thumb_scale);\n        let size = size.to_f64().upscale(thumb_scale);\n\n        // Round to physical pixels.\n        size.to_physical_precise_round(scale).to_logical(scale)\n    }\n\n    fn title_texture(\n        &self,\n        renderer: &mut GlesRenderer,\n        mapped: &Mapped,\n        scale: f64,\n    ) -> Option<MruTexture> {\n        with_toplevel_role(mapped.toplevel(), |role| {\n            role.title\n                .as_ref()\n                .and_then(|title| self.title_texture.borrow_mut().get(renderer, title, scale))\n        })\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    fn render<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        config: &niri_config::RecentWindows,\n        mapped: &Mapped,\n        preview_geo: Rectangle<f64, Logical>,\n        scale: f64,\n        is_active: bool,\n        bob_y: f64,\n        target: RenderTarget,\n        push: &mut dyn FnMut(WindowMruUiRenderElement<R>),\n    ) {\n        let _span = tracy_client::span!(\"Thumbnail::render\");\n\n        let round = move |logical: f64| round_logical_in_physical(scale, logical);\n        let padding = round(config.highlight.padding);\n        let title_gap = round(TITLE_GAP);\n\n        let s = Scale::from(scale);\n\n        let preview_alpha = self\n            .open_animation\n            .as_ref()\n            .map_or(1., |a| a.clamped_value() as f32)\n            .clamp(0., 1.);\n\n        let bob_y = if mapped.rules().baba_is_float == Some(true) {\n            bob_y\n        } else {\n            0.\n        };\n        let bob_offset = Point::new(0., bob_y);\n\n        // Clip thumbnails to their geometry.\n        let radius = if mapped.sizing_mode().is_normal() {\n            mapped.rules().geometry_corner_radius\n        } else {\n            None\n        }\n        .unwrap_or_default();\n\n        let has_border_shader = BorderRenderElement::has_shader(renderer);\n        let clip_shader = ClippedSurfaceRenderElement::shader(renderer).cloned();\n        let geo = Rectangle::from_size(self.size.to_f64());\n        // FIXME: deduplicate code with Tile::render_inner()\n        let clip = move |elem| match elem {\n            LayoutElementRenderElement::Wayland(elem) => {\n                if let Some(shader) = clip_shader.clone() {\n                    if ClippedSurfaceRenderElement::will_clip(&elem, s, geo, radius) {\n                        let elem =\n                            ClippedSurfaceRenderElement::new(elem, s, geo, shader.clone(), radius);\n                        return ThumbnailRenderElement::ClippedSurface(elem);\n                    }\n                }\n\n                // If we don't have the shader, render it normally.\n                let elem = LayoutElementRenderElement::Wayland(elem);\n                ThumbnailRenderElement::LayoutElement(elem)\n            }\n            LayoutElementRenderElement::SolidColor(elem) => {\n                // In this branch we're rendering a blocked-out window with a solid\n                // color. We need to render it with a rounded corner shader even if\n                // clip_to_geometry is false, because in this case we're assuming that\n                // the unclipped window CSD already has corners rounded to the\n                // user-provided radius, so our blocked-out rendering should match that\n                // radius.\n                if radius != CornerRadius::default() && has_border_shader {\n                    return BorderRenderElement::new(\n                        geo.size,\n                        Rectangle::from_size(geo.size),\n                        GradientInterpolation::default(),\n                        Color::from_color32f(elem.color()),\n                        Color::from_color32f(elem.color()),\n                        0.,\n                        Rectangle::from_size(geo.size),\n                        0.,\n                        radius,\n                        scale as f32,\n                        1.,\n                    )\n                    .into();\n                }\n\n                // Otherwise, render the solid color as is.\n                LayoutElementRenderElement::SolidColor(elem).into()\n            }\n        };\n\n        let downscale = move |elem| {\n            let thumb_scale = Scale {\n                x: preview_geo.size.w / geo.size.w,\n                y: preview_geo.size.h / geo.size.h,\n            };\n            let offset = Point::new(\n                preview_geo.size.w - (geo.size.w * thumb_scale.x),\n                preview_geo.size.h - (geo.size.h * thumb_scale.y),\n            )\n            .downscale(2.);\n            let elem = RescaleRenderElement::from_element(elem, Point::new(0, 0), thumb_scale);\n            let elem = RelocateRenderElement::from_element(\n                elem,\n                (preview_geo.loc + offset + bob_offset).to_physical_precise_round(scale),\n                Relocate::Relative,\n            );\n            WindowMruUiRenderElement::Thumbnail(elem)\n        };\n\n        // FIXME: this could use mipmaps, for that it should be rendered through an offscreen.\n        mapped.render_normal(\n            renderer,\n            Point::new(0., 0.),\n            s,\n            preview_alpha,\n            target,\n            &mut |elem| {\n                let elem = clip(elem);\n                let elem = downscale(elem);\n                push(elem)\n            },\n        );\n\n        let mut title_size = None;\n        let title_texture = self.title_texture(renderer.as_gles_renderer(), mapped, scale);\n        let title_texture = title_texture.map(|texture| {\n            let mut size = texture.logical_size();\n            size.w = f64::min(size.w, preview_geo.size.w);\n            title_size = Some(size);\n            (texture, size)\n        });\n\n        // Hide title for blocked-out windows, but only after computing the title size. This way,\n        // the background and the border won't have to oscillate in size between normal and\n        // screencast renders, causing excessive damage.\n        let should_block_out = target.should_block_out(mapped.rules().block_out_from);\n        let title_texture = title_texture.filter(|_| !should_block_out);\n\n        if let Some((texture, size)) = title_texture {\n            // Clip from the right if it doesn't fit.\n            let src = Rectangle::from_size(size);\n\n            let loc = preview_geo.loc\n                + Point::new(\n                    (preview_geo.size.w - size.w) / 2.,\n                    preview_geo.size.h + title_gap,\n                );\n            let loc = loc.to_physical_precise_round(scale).to_logical(scale);\n            let texture = TextureRenderElement::from_texture_buffer(\n                texture,\n                loc,\n                preview_alpha,\n                Some(src),\n                None,\n                Kind::Unspecified,\n            );\n\n            let renderer = renderer.as_gles_renderer();\n            if let Some(program) = GradientFadeTextureRenderElement::shader(renderer) {\n                let elem = GradientFadeTextureRenderElement::new(texture, program);\n                push(WindowMruUiRenderElement::GradientFadeElem(elem));\n            } else {\n                let elem = PrimaryGpuTextureRenderElement(texture);\n                push(WindowMruUiRenderElement::TextureElement(elem));\n            }\n        }\n\n        let is_urgent = mapped.is_urgent();\n        if is_active || is_urgent {\n            let padding = Point::new(padding, padding);\n\n            let mut size = preview_geo.size;\n            size += padding.to_size().upscale(2.);\n\n            if let Some(title_size) = title_size {\n                size.h += title_gap + title_size.h;\n                // Subtract half the padding so it looks more balanced visually.\n                size.h -= round(padding.y / 2.);\n            }\n\n            // FIXME: gradient support (will require passing down correct view_rect).\n            let mut color = if is_urgent {\n                config.highlight.urgent_color\n            } else {\n                config.highlight.active_color\n            };\n            if !is_active {\n                color *= 0.4;\n            }\n\n            let radius = CornerRadius::from(config.highlight.corner_radius as f32);\n\n            let loc = preview_geo.loc - padding;\n\n            let mut background = self.background.borrow_mut();\n            let mut config = *background.config();\n            config.active_color = color;\n            background.update_config(config);\n            background.update_render_elements(\n                size,\n                true,\n                false,\n                false,\n                Rectangle::default(),\n                radius,\n                scale,\n                0.5,\n            );\n            background.render(renderer, loc, &mut |elem| {\n                push(WindowMruUiRenderElement::FocusRing(elem))\n            });\n\n            let mut border = self.border.borrow_mut();\n            let mut config = *border.config();\n            config.off = !is_active;\n            config.width = round(BORDER);\n            config.active_color = color;\n            border.update_config(config);\n            border.set_thicken_corners(false);\n            border.update_render_elements(\n                size,\n                true,\n                true,\n                false,\n                Rectangle::default(),\n                radius.expanded_by(config.width as f32),\n                scale,\n                1.,\n            );\n\n            border.render(renderer, loc, &mut |elem| {\n                push(WindowMruUiRenderElement::FocusRing(elem))\n            });\n        }\n    }\n}\n\nimpl WindowMru {\n    pub fn new(niri: &Niri) -> Self {\n        let Some(output) = niri.layout.active_output() else {\n            return Self {\n                thumbnails: Vec::new(),\n                current_id: None,\n                scope: MruScope::All,\n                app_id_filter: None,\n            };\n        };\n\n        let config = niri.config.borrow().recent_windows.previews;\n        let mut thumbnails = Vec::new();\n        for (mon, ws_idx, ws) in niri.layout.workspaces() {\n            let mon = mon.expect(\"an active output exists so all workspaces have a monitor\");\n            let on_current_output = mon.output() == output;\n            let on_current_workspace = on_current_output && mon.active_workspace_idx() == ws_idx;\n\n            for mapped in ws.windows() {\n                let mut thumbnail = Thumbnail::from_mapped(mapped, niri.clock.clone(), config);\n                thumbnail.on_current_output = on_current_output;\n                thumbnail.on_current_workspace = on_current_workspace;\n                thumbnails.push(thumbnail);\n            }\n        }\n\n        thumbnails\n            .sort_by(|Thumbnail { timestamp: t1, .. }, Thumbnail { timestamp: t2, .. }| t2.cmp(t1));\n\n        let current_id = thumbnails.first().map(|t| t.id);\n        Self {\n            thumbnails,\n            current_id,\n            scope: MruScope::All,\n            app_id_filter: None,\n        }\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.thumbnails.is_empty()\n    }\n\n    #[cfg(test)]\n    fn verify_invariants(&self) {\n        if let Some(id) = self.current_id {\n            assert!(\n                self.thumbnails().any(|thumbnail| thumbnail.id == id),\n                \"current_id must be present in the current filtered thumbnail list\",\n            );\n        } else {\n            assert!(\n                self.thumbnails().next().is_none(),\n                \"unset current_id must mean that the filtered thumbnail list is empty\",\n            );\n        }\n    }\n\n    fn thumbnails(&self) -> impl DoubleEndedIterator<Item = &Thumbnail> {\n        let matches = match_filter(self.scope, self.app_id_filter.as_deref());\n        self.thumbnails.iter().filter(move |t| matches(t))\n    }\n\n    fn thumbnails_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Thumbnail> {\n        let matches = match_filter(self.scope, self.app_id_filter.as_deref());\n        self.thumbnails.iter_mut().filter(move |t| matches(t))\n    }\n\n    fn thumbnails_with_idx(&self) -> impl DoubleEndedIterator<Item = (usize, &Thumbnail)> {\n        let matches = match_filter(self.scope, self.app_id_filter.as_deref());\n        self.thumbnails\n            .iter()\n            .enumerate()\n            .filter(move |(_, t)| matches(t))\n    }\n\n    fn are_animations_ongoing(&self) -> bool {\n        self.thumbnails.iter().any(|t| t.are_animations_ongoing())\n    }\n\n    fn advance_animations(&mut self) {\n        for thumbnail in &mut self.thumbnails {\n            thumbnail.advance_animations();\n        }\n    }\n\n    fn forward(&mut self) {\n        let Some(id) = self.current_id else {\n            return;\n        };\n\n        let next = self.thumbnails().skip_while(|t| t.id != id).nth(1);\n        self.current_id = Some(if let Some(next) = next {\n            next.id\n        } else {\n            // We wrapped around.\n            self.thumbnails().next().unwrap().id\n        });\n    }\n\n    fn backward(&mut self) {\n        let Some(id) = self.current_id else {\n            return;\n        };\n\n        let next = self.thumbnails().rev().skip_while(|t| t.id != id).nth(1);\n        self.current_id = Some(if let Some(next) = next {\n            next.id\n        } else {\n            // We wrapped around.\n            self.thumbnails().next_back().unwrap().id\n        });\n    }\n\n    fn set_current(&mut self, id: MappedId) {\n        if self.thumbnails().any(|thumbnail| thumbnail.id == id) {\n            self.current_id = Some(id);\n        }\n    }\n\n    fn first_id(&self) -> Option<MappedId> {\n        self.thumbnails().next().map(|thumbnail| thumbnail.id)\n    }\n\n    fn first(&mut self) {\n        self.current_id = self.first_id();\n    }\n\n    fn last(&mut self) {\n        let id = self.thumbnails().next_back().map(|thumbnail| thumbnail.id);\n        self.current_id = id;\n    }\n\n    pub fn set_scope(&mut self, scope: MruScope) -> Option<MruScope> {\n        if self.scope == scope {\n            return None;\n        }\n        let rv = Some(self.scope);\n\n        if let Some(id) = self.current_id {\n            let (current_idx, _) = self\n                .thumbnails_with_idx()\n                .find(|(_, thumbnail)| thumbnail.id == id)\n                .unwrap();\n\n            self.scope = scope;\n\n            // Try to select the same, or the first thumbnail to the left. Failing that, select the\n            // first one to the right.\n            let mut id = self.first_id();\n\n            for (idx, thumbnail) in self.thumbnails_with_idx() {\n                if idx > current_idx {\n                    break;\n                }\n                id = Some(thumbnail.id);\n            }\n            self.current_id = id;\n        } else {\n            self.scope = scope;\n            self.current_id = self.first_id();\n        }\n\n        rv\n    }\n\n    pub fn set_filter(&mut self, filter: MruFilter) -> Option<Option<String>> {\n        if self.app_id_filter.is_some() == (filter == MruFilter::AppId) {\n            // Filter unchanged.\n            return None;\n        }\n\n        if let Some(id) = self.current_id {\n            let (current_idx, current_thumbnail) = self\n                .thumbnails_with_idx()\n                .find(|(_, thumbnail)| thumbnail.id == id)\n                .unwrap();\n\n            let old = match filter {\n                MruFilter::All => {\n                    let old = self.app_id_filter.take();\n                    Some(old.expect(\"verified by early return at the top\"))\n                }\n                MruFilter::AppId => {\n                    // If the current thumbnail is missing an app id, we can't set the filter.\n                    let current = current_thumbnail.app_id.clone()?;\n                    let old = self.app_id_filter.replace(current);\n                    assert!(old.is_none(), \"verified by early return at the top\");\n                    None\n                }\n            };\n\n            // Try to select the same, or the first thumbnail to the left. Failing that, select the\n            // first one to the right.\n            let mut id = self.first_id();\n\n            for (idx, thumbnail) in self.thumbnails_with_idx() {\n                if idx > current_idx {\n                    break;\n                }\n                id = Some(thumbnail.id);\n            }\n            self.current_id = id;\n\n            Some(old)\n        } else {\n            match filter {\n                MruFilter::All => {\n                    let old = self.app_id_filter.take();\n                    let old = old.expect(\"verified by early return at the top\");\n                    self.current_id = self.first_id();\n                    Some(Some(old))\n                }\n                MruFilter::AppId => {\n                    // We don't have a current window to set the app id filter.\n                    None\n                }\n            }\n        }\n    }\n\n    fn idx_of(&self, id: MappedId) -> Option<usize> {\n        self.thumbnails.iter().position(|t| t.id == id)\n    }\n\n    fn remove_by_idx(&mut self, idx: usize) -> Option<Thumbnail> {\n        let id = self.thumbnails[idx].id;\n\n        // Try to pick a different window when removing the current one.\n        if self.current_id == Some(id) {\n            self.forward();\n        }\n\n        // If we're still on the same window, that means it's the last visible one.\n        if self.current_id == Some(id) {\n            self.current_id = None;\n        }\n\n        Some(self.thumbnails.remove(idx))\n    }\n\n    /// Returns the thumbnail if it's visible to the left of the currently selected one.\n    fn thumbnail_left_of_current(&self, id: MappedId) -> Option<&Thumbnail> {\n        for thumbnail in self.thumbnails() {\n            if Some(thumbnail.id) == self.current_id {\n                // We found the current window first, so the queried one is *not* to the left.\n                return None;\n            } else if thumbnail.id == id {\n                // We found the queried window first, so the current one is to the right of it.\n                return Some(thumbnail);\n            }\n        }\n        None\n    }\n}\n\nfn matches(scope: MruScope, app_id_filter: Option<&str>, thumbnail: &Thumbnail) -> bool {\n    let x = match scope {\n        MruScope::All => true,\n        MruScope::Output => thumbnail.on_current_output,\n        MruScope::Workspace => thumbnail.on_current_workspace,\n    };\n    if !x {\n        return false;\n    }\n\n    if let Some(app_id) = app_id_filter {\n        thumbnail.app_id.as_deref() == Some(app_id)\n    } else {\n        true\n    }\n}\n\nfn match_filter(scope: MruScope, app_id_filter: Option<&str>) -> impl Fn(&Thumbnail) -> bool + '_ {\n    move |thumbnail| matches(scope, app_id_filter, thumbnail)\n}\n\nimpl ViewPos {\n    fn current(&self) -> f64 {\n        match self {\n            ViewPos::Static(pos) => *pos,\n            ViewPos::Animation(anim) => anim.value(),\n        }\n    }\n\n    fn target(&self) -> f64 {\n        match self {\n            ViewPos::Static(pos) => *pos,\n            ViewPos::Animation(anim) => anim.to(),\n        }\n    }\n\n    fn are_animations_ongoing(&self) -> bool {\n        match self {\n            ViewPos::Static(_) => false,\n            ViewPos::Animation(_) => true,\n        }\n    }\n\n    fn advance_animations(&mut self) {\n        if let ViewPos::Animation(anim) = self {\n            if anim.is_done() {\n                *self = ViewPos::Static(anim.to());\n            }\n        }\n    }\n\n    fn animate_from_with_config(\n        &mut self,\n        from: f64,\n        config: niri_config::Animation,\n        clock: Clock,\n    ) {\n        // FIXME: also compute and use current velocity.\n        let anim = Animation::new(clock, self.current() + from, self.target(), 0., config);\n        *self = ViewPos::Animation(anim);\n    }\n\n    fn offset(&mut self, delta: f64) {\n        match self {\n            ViewPos::Static(pos) => *pos += delta,\n            ViewPos::Animation(anim) => anim.offset(delta),\n        }\n    }\n}\n\nimpl WindowMruUi {\n    pub fn new(config: Rc<RefCell<Config>>) -> Self {\n        let mut rv = Self {\n            state: UiState::Closed {\n                previous_scope: MruScope::default(),\n            },\n            preset_opened_binds: make_preset_opened_binds(),\n            dynamic_opened_binds: Vec::new(),\n            config,\n        };\n        rv.update_binds();\n        rv\n    }\n\n    pub fn update_binds(&mut self) {\n        self.dynamic_opened_binds = make_dynamic_opened_binds(&self.config.borrow());\n    }\n\n    pub fn update_config(&mut self) {\n        let inner = match &mut self.state {\n            UiState::Open(inner) => inner,\n            UiState::Closing { inner, .. } => inner,\n            UiState::Closed { .. } => return,\n        };\n        inner.update_config();\n    }\n\n    pub fn is_open(&self) -> bool {\n        matches!(self.state, UiState::Open { .. })\n    }\n\n    pub fn open(&mut self, clock: Clock, wmru: WindowMru, output: Output) {\n        if self.is_open() {\n            return;\n        }\n\n        let open_delay = self.config.borrow().recent_windows.open_delay_ms;\n        let open_delay = Duration::from_millis(u64::from(open_delay));\n\n        let mut inner = Inner {\n            wmru,\n            view_pos: ViewPos::Static(0.),\n            freeze_view: false,\n            open_at: clock.now_unadjusted() + open_delay,\n            clock,\n            config: self.config.clone(),\n            output,\n            scope_panel: Default::default(),\n            backdrop_buffers: Default::default(),\n            offscreen: OffscreenBuffer::default(),\n        };\n        inner.view_pos = ViewPos::Static(inner.compute_view_pos());\n\n        self.state = UiState::Open(inner);\n    }\n\n    pub fn close(&mut self, close_request: MruCloseRequest) -> Option<MappedId> {\n        if !self.is_open() {\n            return None;\n        }\n        let state = mem::replace(\n            &mut self.state,\n            UiState::Closed {\n                previous_scope: MruScope::default(),\n            },\n        );\n        let UiState::Open(inner) = state else {\n            unreachable!();\n        };\n\n        let response = match close_request {\n            MruCloseRequest::Cancel => None,\n            MruCloseRequest::Confirm => inner.wmru.current_id,\n        };\n\n        if !inner.is_fully_open() {\n            // Hasn't displayed yet, no need to fade out.\n            let UiState::Closed { previous_scope } = &mut self.state else {\n                unreachable!()\n            };\n            *previous_scope = inner.wmru.scope;\n            return response;\n        }\n\n        let config = self.config.borrow();\n        let config = config.animations.recent_windows_close.0;\n\n        let anim = Animation::new(inner.clock.clone(), 1., 0., 0., config);\n        self.state = UiState::Closing { inner, anim };\n        response\n    }\n\n    pub fn advance(&mut self, dir: MruDirection, filter: Option<MruFilter>) {\n        let UiState::Open(inner) = &mut self.state else {\n            return;\n        };\n        inner.freeze_view = false;\n\n        if let Some(filter) = filter {\n            inner.set_filter(filter);\n        }\n\n        match dir {\n            MruDirection::Forward => inner.wmru.forward(),\n            MruDirection::Backward => inner.wmru.backward(),\n        }\n    }\n\n    pub fn set_scope(&mut self, scope: MruScope) {\n        let UiState::Open(inner) = &mut self.state else {\n            return;\n        };\n        inner.freeze_view = false;\n        inner.set_scope(scope);\n    }\n\n    pub fn cycle_scope(&mut self) {\n        let UiState::Open(inner) = &mut self.state else {\n            return;\n        };\n\n        let scope = inner.wmru.scope;\n        let scope = SCOPE_CYCLE\n            .into_iter()\n            .cycle()\n            .skip_while(|s| *s != scope)\n            .nth(1)\n            .unwrap();\n        self.set_scope(scope);\n    }\n\n    pub fn pointer_motion(&mut self, pos_within_output: Point<f64, Logical>) -> Option<MappedId> {\n        let UiState::Open(inner) = &mut self.state else {\n            return None;\n        };\n        // Don't handle pointer until the UI is visible.\n        if !inner.is_fully_open() {\n            return None;\n        }\n\n        inner.freeze_view = true;\n\n        let id = inner.thumbnail_under(pos_within_output);\n        if let Some(id) = id {\n            inner.wmru.set_current(id);\n        }\n        id\n    }\n\n    pub fn first(&mut self) {\n        let UiState::Open(inner) = &mut self.state else {\n            return;\n        };\n        inner.freeze_view = false;\n        inner.wmru.first();\n    }\n\n    pub fn last(&mut self) {\n        let UiState::Open(inner) = &mut self.state else {\n            return;\n        };\n        inner.freeze_view = false;\n        inner.wmru.last();\n    }\n\n    pub fn scope(&self) -> MruScope {\n        match &self.state {\n            UiState::Closed { previous_scope, .. } => *previous_scope,\n            UiState::Open(inner) | UiState::Closing { inner, .. } => inner.wmru.scope,\n        }\n    }\n\n    pub fn current_window_id(&self) -> Option<MappedId> {\n        let UiState::Open(inner) = &self.state else {\n            return None;\n        };\n        inner.wmru.current_id\n    }\n\n    pub fn update_window(&mut self, layout: &Layout<Mapped>, id: MappedId) {\n        let UiState::Open(inner) = &mut self.state else {\n            return;\n        };\n        inner.update_window(layout, id);\n    }\n\n    pub fn remove_window(&mut self, id: MappedId) {\n        let UiState::Open(inner) = &mut self.state else {\n            return;\n        };\n\n        let Some(_thumbnail) = inner.remove_window(id) else {\n            return;\n        };\n\n        if inner.wmru.thumbnails.is_empty() {\n            self.close(MruCloseRequest::Cancel);\n        }\n    }\n\n    pub fn render_output<R: NiriRenderer>(\n        &self,\n        niri: &Niri,\n        output: &Output,\n        renderer: &mut R,\n        target: RenderTarget,\n        push: &mut dyn FnMut(WindowMruUiRenderElement<R>),\n    ) {\n        let (inner, progress) = match &self.state {\n            UiState::Closed { .. } => return,\n            UiState::Closing { inner, anim } => (inner, anim.clamped_value()),\n            UiState::Open(inner) => {\n                if inner.is_fully_open() {\n                    (inner, 1.)\n                } else {\n                    return;\n                }\n            }\n        };\n\n        let _span = tracy_client::span!(\"WindowMruUi::render_output\");\n\n        let alpha = progress.clamp(0., 1.) as f32;\n\n        // Put a backdrop above the current desktop view to contrast the thumbnails.\n        let mut buffers = inner.backdrop_buffers.borrow_mut();\n        let buffer = buffers.entry(output.clone()).or_default();\n        buffer.resize(output_size(output));\n        buffer.set_color(BACKDROP_COLOR);\n        let render_backdrop = |alpha| {\n            SolidColorRenderElement::from_buffer(\n                buffer,\n                Point::new(0., 0.),\n                alpha,\n                Kind::Unspecified,\n            )\n            // Can't wrap into WindowMruUiRenderElement::SolidColor() right here since we have\n            // different <R> generic in offscreen vs. normal path.\n        };\n\n        // During the closing fade, use an offscreen to avoid transparent compositing artifacts.\n        let mut pushed_offscreen = false;\n        if *output == inner.output && alpha < 1. {\n            let renderer = renderer.as_gles_renderer();\n\n            let mut elems = Vec::new();\n            inner.render(niri, renderer, target, &mut |elem| elems.push(elem));\n            elems.push(WindowMruUiRenderElement::SolidColor(render_backdrop(1.)));\n\n            let scale = output.current_scale().fractional_scale();\n            match inner.offscreen.render(renderer, Scale::from(scale), &elems) {\n                Ok((elem, _sync, _data)) => {\n                    // FIXME: would be good to passthrough offscreen data to visible windows here.\n                    // As is, during the closing fade, windows from other workspaces stop receiving\n                    // frame callbacks.\n                    //\n                    // However, we need to refactor our offscreen data a bit to make this nicer.\n                    // Currently it supports a stack of offscreens, but not a several unrelated\n                    // offscreens showing the same window (possibly in addition to the window\n                    // itself).\n                    //\n                    // Anyhow, this is not very noticeable since Alt-Tab closing happens quickly.\n                    push(WindowMruUiRenderElement::Offscreen(elem.with_alpha(alpha)));\n                    pushed_offscreen = true;\n                }\n                Err(err) => {\n                    warn!(\"error rendering MRU to offscreen for fade-out: {err:?}\");\n                }\n            }\n        }\n\n        // When alpha is 1., render everything directly, without an offscreen.\n        //\n        // This is not used as fallback when offscreen fails to render because it looks better to\n        // hide the previews immediately than to render them with alpha = 1. during a fade-out.\n        if *output == inner.output && alpha == 1. {\n            inner.render(niri, renderer, target, &mut |elem| push(elem));\n        }\n\n        // This is used for both normal elems and for other outputs.\n        if !pushed_offscreen {\n            push(WindowMruUiRenderElement::SolidColor(render_backdrop(alpha)));\n        }\n    }\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        match &self.state {\n            UiState::Open(inner) => inner.are_animations_ongoing(),\n            UiState::Closing { .. } => true,\n            UiState::Closed { .. } => false,\n        }\n    }\n\n    pub fn advance_animations(&mut self) {\n        match &mut self.state {\n            UiState::Open(inner) => inner.advance_animations(),\n            UiState::Closing { inner, anim } => {\n                if anim.is_done() {\n                    self.state = UiState::Closed {\n                        previous_scope: inner.wmru.scope,\n                    };\n                    return;\n                }\n                inner.advance_animations();\n            }\n            UiState::Closed { .. } => {}\n        }\n    }\n\n    pub fn opened_bindings(&mut self, mods: Modifiers) -> impl Iterator<Item = &Bind> + Clone {\n        // Fill modifiers with the current mods.\n        for bind in &mut self.preset_opened_binds {\n            bind.key.modifiers = mods;\n        }\n        for bind in &mut self.dynamic_opened_binds {\n            bind.key.modifiers = mods;\n        }\n\n        self.preset_opened_binds\n            .iter()\n            .chain(&self.dynamic_opened_binds)\n    }\n\n    pub fn output(&self) -> Option<&Output> {\n        match &self.state {\n            UiState::Open(inner) => Some(&inner.output),\n            _ => None,\n        }\n    }\n\n    #[cfg(feature = \"dbus\")]\n    pub fn a11y_scope_text(&self) -> String {\n        let scope = match self.scope() {\n            MruScope::All => \"all\",\n            MruScope::Output => \"output\",\n            MruScope::Workspace => \"workspace\",\n        };\n        format!(\"Scope {scope}\")\n    }\n}\n\nfn compute_view_offset(cur_x: f64, working_width: f64, new_col_x: f64, new_col_width: f64) -> f64 {\n    let new_x = new_col_x;\n    let new_right_x = new_col_x + new_col_width;\n\n    // If the column is already fully visible, leave the view as is.\n    if cur_x <= new_x && new_right_x <= cur_x + working_width {\n        return -(new_col_x - cur_x);\n    }\n\n    // Otherwise, prefer the alignment that results in less motion from the current position.\n    let dist_to_left = (cur_x - new_x).abs();\n    let dist_to_right = ((cur_x + working_width) - new_right_x).abs();\n    if dist_to_left <= dist_to_right {\n        0.\n    } else {\n        -(working_width - new_col_width)\n    }\n}\n\nimpl Inner {\n    fn update_config(&mut self) {\n        self.freeze_view = false;\n\n        let config = self.config.borrow().recent_windows.previews;\n        for thumbnail in &mut self.wmru.thumbnails {\n            thumbnail.config = config;\n        }\n    }\n\n    fn are_animations_ongoing(&self) -> bool {\n        self.clock.now_unadjusted() < self.open_at\n            || self.view_pos.are_animations_ongoing()\n            || self.wmru.are_animations_ongoing()\n    }\n\n    fn advance_animations(&mut self) {\n        self.view_pos.advance_animations();\n        self.wmru.advance_animations();\n\n        if !self.freeze_view {\n            let new_view_pos = self.compute_view_pos();\n            let delta = new_view_pos - self.view_pos.target();\n            let pixel = 1. / self.output.current_scale().fractional_scale();\n            if delta.abs() > pixel {\n                self.animate_view_pos_from(-delta);\n            }\n            self.view_pos.offset(delta);\n        }\n    }\n\n    fn is_fully_open(&self) -> bool {\n        self.open_at <= self.clock.now_unadjusted()\n    }\n\n    fn animate_view_pos_from(&mut self, from: f64) {\n        let config = self.config.borrow().animations.window_movement.0;\n        self.view_pos\n            .animate_from_with_config(from, config, self.clock.clone());\n    }\n\n    fn compute_view_pos(&self) -> f64 {\n        let Some(current_id) = self.wmru.current_id else {\n            return 0.;\n        };\n\n        let output_size = output_size(&self.output);\n\n        let working_x = STRUT + GAP;\n        let working_width = (output_size.w - working_x * 2.).max(0.);\n\n        let mut current_geo = Rectangle::default();\n        let mut strip_width = 0.;\n        for (thumbnail, geo) in self.thumbnails() {\n            if thumbnail.id == current_id {\n                current_geo = geo;\n            }\n            strip_width = geo.loc.x + geo.size.w;\n\n            // If we found current_geo, and the strip width is already bigger than the working\n            // width, no need to compute further.\n            if current_geo.size.w != 0. && strip_width > working_width {\n                break;\n            }\n        }\n\n        // If the whole strip fits on screen, center it.\n        if strip_width <= working_width {\n            return -(output_size.w - strip_width) / 2.;\n        }\n\n        compute_view_offset(\n            self.view_pos.target() + working_x,\n            working_width,\n            current_geo.loc.x,\n            current_geo.size.w,\n        ) + current_geo.loc.x\n            - working_x\n    }\n\n    fn update_window(&mut self, layout: &Layout<Mapped>, id: MappedId) {\n        let output_size = output_size(&self.output);\n        let scale = self.output.current_scale().fractional_scale();\n\n        // If the updated window is to the left of the currently selected one, we need to offset\n        // the view position to compensate for the change in size.\n        let left = self.wmru.thumbnail_left_of_current(id);\n        let prev_size = left.map(|thumbnail| thumbnail.preview_size(output_size, scale));\n\n        let Some(thumbnail) = self.wmru.thumbnails.iter_mut().find(|t| t.id == id) else {\n            return;\n        };\n\n        let Some((_, mapped)) = layout.windows().find(|(_, m)| m.id() == id) else {\n            error!(\"window in the MRU must be present in the layout\");\n            return;\n        };\n\n        thumbnail.update_window(mapped);\n\n        if let Some(prev) = prev_size {\n            let new = thumbnail.preview_size(output_size, scale);\n            let delta = new.w - prev.w;\n            self.view_pos.offset(delta);\n        }\n    }\n\n    fn remove_window(&mut self, id: MappedId) -> Option<Thumbnail> {\n        let idx = self.wmru.idx_of(id)?;\n\n        let last_visible = self.wmru.thumbnails().next_back();\n        let removing_last_visible = last_visible.is_some_and(|t| t.id == id);\n\n        // When removing the last visible thumbnail, nothing needs to be animated.\n        // - If it's not currently selected, then it can't cause changes to view position.\n        // - If it's currently selected, then the first step in removal (focusing the next window)\n        //   will wrap back to the start, and no animations should happen.\n        if !removing_last_visible {\n            let output_size = output_size(&self.output);\n            let scale = self.output.current_scale().fractional_scale();\n            let round = move |logical: f64| round_logical_in_physical(scale, logical);\n\n            let padding = self.config.borrow().recent_windows.highlight.padding;\n            let padding = round(padding) + round(BORDER);\n            let gap = padding + round(GAP) + padding;\n\n            let prev_size = self.wmru.thumbnails[idx].preview_size(output_size, scale);\n            let delta = prev_size.w + gap;\n\n            let config = self.config.borrow().animations.window_movement.0;\n\n            // If the removed window is to the left of the currently selected one, we need to offset\n            // the view position to compensate for the change.\n            if self.wmru.thumbnail_left_of_current(id).is_some() {\n                self.view_pos.offset(-delta);\n\n                // And animate movement of windows left of it.\n                for thumbnail in self.wmru.thumbnails_mut().take_while(|t| t.id != id) {\n                    thumbnail.animate_move_from_with_config(-delta, config);\n                }\n            } else {\n                // Otherwise, animate movement of windows right of it.\n                for thumbnail in self.wmru.thumbnails_mut().rev().take_while(|t| t.id != id) {\n                    thumbnail.animate_move_from_with_config(delta, config);\n                }\n            }\n        }\n\n        self.wmru.remove_by_idx(idx)\n    }\n\n    fn set_scope(&mut self, scope: MruScope) {\n        let was_empty = self.wmru.current_id.is_none();\n        if let Some(old_scope) = self.wmru.set_scope(scope) {\n            self.animate_scope_filter_change(was_empty, old_scope, None);\n        }\n    }\n\n    fn set_filter(&mut self, filter: MruFilter) {\n        let was_empty = self.wmru.current_id.is_none();\n        if let Some(old_filter) = self.wmru.set_filter(filter) {\n            let old_filter = Some(old_filter.as_deref());\n            self.animate_scope_filter_change(was_empty, self.wmru.scope, old_filter);\n        }\n    }\n\n    fn animate_scope_filter_change(\n        &mut self,\n        was_empty: bool,\n        old_scope: MruScope,\n        old_filter: Option<Option<&str>>,\n    ) {\n        let Some(id) = self.wmru.current_id else {\n            // If there's no current_id then the new filter caused all windows to disappear, so\n            // there's nothing to animate.\n            return;\n        };\n        let idx = self.wmru.idx_of(id).unwrap();\n\n        // Animate opening for newly appeared thumbnails.\n        let config = self.config.borrow().animations.window_open.anim;\n        let old_filter = old_filter.unwrap_or(self.wmru.app_id_filter.as_deref());\n        let matches_old = match_filter(old_scope, old_filter);\n        let matches_new = match_filter(self.wmru.scope, self.wmru.app_id_filter.as_deref());\n        for thumbnail in &mut self.wmru.thumbnails {\n            if matches_new(thumbnail) && !matches_old(thumbnail) {\n                thumbnail.animate_open_with_config(config);\n            }\n        }\n\n        if was_empty {\n            self.view_pos = ViewPos::Static(self.compute_view_pos());\n            return;\n        }\n\n        let output_size = output_size(&self.output);\n        let scale = self.output.current_scale().fractional_scale();\n        let round = move |logical: f64| round_logical_in_physical(scale, logical);\n\n        let padding = self.config.borrow().recent_windows.highlight.padding;\n        let padding = round(padding) + round(BORDER);\n        let gap = padding + round(GAP) + padding;\n\n        let config = self.config.borrow().animations.window_movement.0;\n\n        let mut delta = 0.;\n        for t in &mut self.wmru.thumbnails[idx + 1..] {\n            match (matches_old(t), matches_new(t)) {\n                (true, true) => t.animate_move_from_with_config(delta, config),\n                (true, false) => delta += t.preview_size(output_size, scale).w + gap,\n                (false, true) => delta -= t.preview_size(output_size, scale).w + gap,\n                (false, false) => (),\n            }\n        }\n\n        let mut delta = 0.;\n        for t in self.wmru.thumbnails[..idx].iter_mut().rev() {\n            match (matches_old(t), matches_new(t)) {\n                (true, true) => t.animate_move_from_with_config(-delta, config),\n                (true, false) => delta += t.preview_size(output_size, scale).w + gap,\n                (false, true) => delta -= t.preview_size(output_size, scale).w + gap,\n                (false, false) => (),\n            }\n        }\n\n        self.view_pos.offset(-delta);\n    }\n\n    fn thumbnails(&self) -> impl Iterator<Item = (&Thumbnail, Rectangle<f64, Logical>)> {\n        let output_size = output_size(&self.output);\n        let scale = self.output.current_scale().fractional_scale();\n        let round = move |logical: f64| round_logical_in_physical(scale, logical);\n\n        let padding = self.config.borrow().recent_windows.highlight.padding;\n        let padding = round(padding) + round(BORDER);\n        let gap = padding + round(GAP) + padding;\n\n        let mut x = 0.;\n        self.wmru.thumbnails().map(move |thumbnail| {\n            let size = thumbnail.preview_size(output_size, scale);\n            let y = round((output_size.h - size.h) / 2.);\n\n            let loc = Point::new(x, y);\n            x += size.w + gap;\n\n            let geo = Rectangle::new(loc, size);\n            (thumbnail, geo)\n        })\n    }\n\n    fn thumbnails_in_view_static(\n        &self,\n    ) -> impl Iterator<Item = (&Thumbnail, Rectangle<f64, Logical>)> {\n        let output_size = output_size(&self.output);\n        let scale = self.output.current_scale().fractional_scale();\n        let round = |logical: f64| round_logical_in_physical(scale, logical);\n\n        let view_pos = round(self.view_pos.current());\n\n        let leftmost = view_pos;\n        let rightmost = view_pos + output_size.w;\n\n        self.thumbnails()\n            .skip_while(move |(_, geo)| geo.loc.x + geo.size.w <= leftmost)\n            .map_while(move |(thumbnail, mut geo)| {\n                if rightmost <= geo.loc.x {\n                    return None;\n                }\n\n                geo.loc.x -= view_pos;\n                Some((thumbnail, geo))\n            })\n    }\n\n    fn thumbnails_in_view_render(\n        &self,\n    ) -> impl Iterator<Item = (&Thumbnail, Rectangle<f64, Logical>)> {\n        let output_size = output_size(&self.output);\n        let scale = self.output.current_scale().fractional_scale();\n        let round = move |logical: f64| round_logical_in_physical(scale, logical);\n\n        let view_pos = round(self.view_pos.current());\n\n        self.thumbnails().filter_map(move |(thumbnail, mut geo)| {\n            geo.loc.x -= view_pos;\n            geo.loc.x += round(thumbnail.render_offset());\n\n            if geo.loc.x + geo.size.w < 0. || output_size.w < geo.loc.x {\n                return None;\n            }\n\n            Some((thumbnail, geo))\n        })\n    }\n\n    fn render<R: NiriRenderer>(\n        &self,\n        niri: &Niri,\n        renderer: &mut R,\n        target: RenderTarget,\n        push: &mut dyn FnMut(WindowMruUiRenderElement<R>),\n    ) {\n        let output_size = output_size(&self.output);\n        let scale = self.output.current_scale().fractional_scale();\n\n        let panel_texture =\n            self.scope_panel\n                .borrow_mut()\n                .get(renderer.as_gles_renderer(), scale, self.wmru.scope);\n        if let Some(texture) = panel_texture {\n            let padding = round_logical_in_physical(scale, f64::from(PANEL_PADDING));\n\n            let size = texture.logical_size();\n            let location = Point::new((output_size.w - size.w) / 2., padding * 2.);\n            let elem = PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(\n                texture.clone(),\n                location,\n                1.,\n                None,\n                None,\n                Kind::Unspecified,\n            ));\n            push(WindowMruUiRenderElement::TextureElement(elem));\n        }\n\n        let current_id = self.wmru.current_id;\n\n        let bob_y = baba_is_float_offset(self.clock.now(), output_size.h);\n        let bob_y = round_logical_in_physical(scale, bob_y);\n\n        let config = self.config.borrow();\n\n        for (thumbnail, geo) in self.thumbnails_in_view_render() {\n            let id = thumbnail.id;\n            let Some((_, mapped)) = niri.layout.windows().find(|(_, m)| m.id() == id) else {\n                error!(\"window in the MRU must be present in the layout\");\n                continue;\n            };\n\n            let config = &config.recent_windows;\n\n            let is_active = Some(id) == current_id;\n            thumbnail.render(\n                renderer, config, mapped, geo, scale, is_active, bob_y, target, push,\n            );\n        }\n    }\n\n    fn thumbnail_under(&self, pos: Point<f64, Logical>) -> Option<MappedId> {\n        let scale = self.output.current_scale().fractional_scale();\n        let round = move |logical: f64| round_logical_in_physical(scale, logical);\n        let padding = self.config.borrow().recent_windows.highlight.padding;\n        let padding = round(padding) + round(BORDER);\n        let padding = Point::new(padding, padding);\n        let title_gap = round(TITLE_GAP);\n\n        for (thumbnail, mut geo) in self.thumbnails_in_view_static() {\n            geo.loc -= padding;\n            geo.size += padding.to_size().upscale(2.);\n\n            // It doesn't really matter all that much if the title texture is stale here, and it\n            // would be annoying to thread the rendering into this function. The texture might be\n            // one frame stale or so.\n            if let Some(texture) = thumbnail.title_texture.borrow().get_stale() {\n                let title_size = texture.logical_size();\n                geo.size.h += title_gap + title_size.h;\n                // Subtract half the padding so it looks more balanced visually.\n                geo.size.h -= round(padding.y / 2.);\n            }\n\n            if geo.contains(pos) {\n                return Some(thumbnail.id);\n            }\n        }\n\n        None\n    }\n}\n\nimpl TitleTexture {\n    fn get(&mut self, renderer: &mut GlesRenderer, title: &str, scale: f64) -> Option<MruTexture> {\n        if self.title != title || self.scale != scale {\n            self.texture = None;\n            self.title = title.to_owned();\n            self.scale = scale;\n        }\n\n        self.texture\n            .get_or_insert_with(|| generate_title_texture(renderer, title, scale).ok())\n            .clone()\n    }\n\n    fn get_stale(&self) -> Option<&MruTexture> {\n        if let Some(Some(texture)) = &self.texture {\n            Some(texture)\n        } else {\n            None\n        }\n    }\n}\n\nfn generate_title_texture(\n    renderer: &mut GlesRenderer,\n    title: &str,\n    scale: f64,\n) -> anyhow::Result<MruTexture> {\n    let _span = tracy_client::span!(\"mru::generate_title_texture\");\n\n    let mut font = FontDescription::from_string(FONT);\n    font.set_absolute_size(to_physical_precise_round(scale, font.size()));\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, 0, 0)?;\n    let cr = cairo::Context::new(&surface)?;\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    // On Window CSD, line breaks are either stripped or replaced with the linebreak symbol anyway.\n    // No use rendering it as multiple lines.\n    layout.set_single_paragraph_mode(true);\n    layout.set_font_description(Some(&font));\n    layout.set_text(title);\n\n    let (width, height) = layout.pixel_size();\n    ensure!(width > 0 && height > 0);\n\n    // Guard against overly long window titles.\n    let width = min(width, 16383);\n    let height = min(height, 16383);\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;\n    let cr = cairo::Context::new(&surface)?;\n    cr.set_source_rgb(1., 1., 1.);\n    pangocairo::functions::show_layout(&cr, &layout);\n\n    drop(cr);\n    let data = surface.take_data().unwrap();\n    let buffer = TextureBuffer::from_memory(\n        renderer,\n        &data,\n        Fourcc::Argb8888,\n        (width, height),\n        false,\n        scale,\n        Transform::Normal,\n        Vec::new(),\n    )?;\n\n    Ok(buffer)\n}\n\nimpl ScopePanel {\n    fn get(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        scale: f64,\n        scope: MruScope,\n    ) -> Option<MruTexture> {\n        if self.scale != scale {\n            self.textures = None;\n            self.scale = scale;\n        }\n\n        self.textures\n            .get_or_insert_with(|| generate_scope_panels(renderer, scale).ok())\n            .as_ref()\n            .map(|x| x[scope as usize].clone())\n    }\n}\n\nfn generate_scope_panels(\n    renderer: &mut GlesRenderer,\n    scale: f64,\n) -> anyhow::Result<[MruTexture; 3]> {\n    fn make_panel_text(idx: usize) -> String {\n        let span_unselected = \"<span fgcolor='#999999'>\";\n        let span_end = \"</span>\";\n        let span_shortcut = \"<span face='mono' bgcolor='#2C2C2C' letter_spacing='5000'><b>\";\n        let span_shortcut_end = \"</b></span>\";\n\n        // Starts with a zero-width space to make letter_spacing work on the left.\n        let mut buf =\n            format!(\"\\u{200B}{span_unselected}{span_shortcut}S{span_shortcut_end}cope:{span_end}\");\n\n        for scope in SCOPE_CYCLE {\n            buf.push_str(\"  \");\n            if scope as usize != idx {\n                buf.push_str(span_unselected);\n            }\n            let text = match scope {\n                MruScope::All => format!(\"{span_shortcut}A{span_shortcut_end}ll\"),\n                MruScope::Output => format!(\"{span_shortcut}O{span_shortcut_end}utput\"),\n                MruScope::Workspace => format!(\"{span_shortcut}W{span_shortcut_end}orkspace\"),\n            };\n            buf.push_str(&text);\n            if scope as usize != idx {\n                buf.push_str(span_end);\n            }\n        }\n\n        buf\n    }\n\n    // Can't wait for array::try_map()\n    Ok([\n        render_panel(renderer, scale, &make_panel_text(0))?,\n        render_panel(renderer, scale, &make_panel_text(1))?,\n        render_panel(renderer, scale, &make_panel_text(2))?,\n    ])\n}\n\nfn render_panel(renderer: &mut GlesRenderer, scale: f64, text: &str) -> anyhow::Result<MruTexture> {\n    let _span = tracy_client::span!(\"mru::render_panel\");\n\n    let mut font = FontDescription::from_string(FONT);\n    font.set_absolute_size(to_physical_precise_round(scale, font.size()));\n\n    let padding: i32 = to_physical_precise_round(scale, PANEL_PADDING);\n    // Keep the border width even to avoid blurry edges.\n    // Render to a dummy surface to determine the size.\n    let surface = ImageSurface::create(cairo::Format::ARgb32, 0, 0)?;\n    let cr = cairo::Context::new(&surface)?;\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_markup(text);\n    let (mut width, mut height) = layout.pixel_size();\n\n    width += padding * 2;\n    height += padding * 2;\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;\n    let cr = cairo::Context::new(&surface)?;\n    cr.set_source_rgb(0.1, 0.1, 0.1);\n    cr.paint()?;\n\n    let padding = f64::from(padding);\n\n    cr.move_to(padding, padding);\n\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_markup(text);\n\n    cr.set_source_rgb(1., 1., 1.);\n    pangocairo::functions::show_layout(&cr, &layout);\n\n    cr.move_to(0., 0.);\n    cr.line_to(width.into(), 0.);\n    cr.line_to(width.into(), height.into());\n    cr.line_to(0., height.into());\n    cr.line_to(0., 0.);\n    cr.set_source_rgb(0.5, 0.5, 0.5);\n    cr.set_line_width((f64::from(PANEL_BORDER) / 2. * scale).round() * 2.);\n    cr.stroke()?;\n\n    drop(cr);\n    let data = surface.take_data().unwrap();\n    let buffer = TextureBuffer::from_memory(\n        renderer,\n        &data,\n        Fourcc::Argb8888,\n        (width, height),\n        false,\n        scale,\n        Transform::Normal,\n        Vec::new(),\n    )?;\n\n    Ok(buffer)\n}\n\n/// Returns key bindings available when the MRU UI is open.\nfn make_preset_opened_binds() -> Vec<Bind> {\n    let mut rv = Vec::new();\n\n    let mut push = |trigger, action| {\n        rv.push(Bind {\n            key: Key {\n                trigger: Trigger::Keysym(trigger),\n                // The modifier is filled dynamically.\n                modifiers: Modifiers::empty(),\n            },\n            action,\n            repeat: true,\n            cooldown: None,\n            allow_when_locked: false,\n            allow_inhibiting: false,\n            hotkey_overlay_title: None,\n        })\n    };\n\n    push(Keysym::Escape, Action::MruCancel);\n    push(Keysym::Return, Action::MruConfirm);\n    push(Keysym::space, Action::MruConfirm);\n    push(Keysym::a, Action::MruSetScope(MruScope::All));\n    push(Keysym::o, Action::MruSetScope(MruScope::Output));\n    push(Keysym::w, Action::MruSetScope(MruScope::Workspace));\n    push(Keysym::s, Action::MruCycleScope);\n\n    // Leave these in since they are the most expected and generally uncontroversial keys, so that\n    // they work even if these actions are absent from the normal binds.\n    push(Keysym::Home, Action::MruFirst);\n    push(Keysym::End, Action::MruLast);\n    push(\n        Keysym::Left,\n        Action::MruAdvance {\n            direction: MruDirection::Backward,\n            scope: None,\n            filter: None,\n        },\n    );\n    push(\n        Keysym::Right,\n        Action::MruAdvance {\n            direction: MruDirection::Forward,\n            scope: None,\n            filter: None,\n        },\n    );\n\n    rv\n}\n\n/// Returns dynamic key bindings available when the MRU UI is open.\n///\n/// These ones are generated based on the normal bindings.\nfn make_dynamic_opened_binds(config: &Config) -> Vec<Bind> {\n    let mut binds: HashMap<Trigger, Vec<Bind>> = HashMap::new();\n\n    for bind in &config.binds.0 {\n        let action = match &bind.action {\n            Action::FocusColumnRight\n            | Action::FocusColumnRightOrFirst\n            | Action::FocusColumnOrMonitorRight\n            | Action::FocusWindowDownOrColumnRight => Action::MruAdvance {\n                direction: MruDirection::Forward,\n                scope: None,\n                filter: None,\n            },\n            Action::FocusColumnLeft\n            | Action::FocusColumnLeftOrLast\n            | Action::FocusColumnOrMonitorLeft\n            | Action::FocusWindowUpOrColumnLeft => Action::MruAdvance {\n                direction: MruDirection::Backward,\n                scope: None,\n                filter: None,\n            },\n            Action::FocusColumnFirst => Action::MruFirst,\n            Action::FocusColumnLast => Action::MruLast,\n            Action::CloseWindow => Action::MruCloseCurrentWindow,\n            x @ Action::Screenshot(_, _) => x.clone(),\n            _ => continue,\n        };\n\n        binds.entry(bind.key.trigger).or_default().push(Bind {\n            action,\n            ..bind.clone()\n        });\n    }\n\n    let mut rv = Vec::new();\n\n    // For each trigger, take the bind with the lowest number of modifiers.\n    for binds in binds.into_values() {\n        let bind = binds\n            .into_iter()\n            .min_by_key(|bind| bind.key.modifiers.iter().count())\n            .unwrap();\n\n        rv.push(Bind {\n            key: Key {\n                trigger: bind.key.trigger,\n                // The modifier is filled dynamically.\n                modifiers: Modifiers::empty(),\n            },\n            ..bind\n        });\n    }\n\n    rv\n}\n"
  },
  {
    "path": "src/ui/screen_transition.rs",
    "content": "use std::time::Duration;\n\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::GlesTexture;\nuse smithay::utils::{Scale, Transform};\n\nuse crate::animation::Clock;\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};\nuse crate::render_helpers::RenderTarget;\n\npub const DELAY: Duration = Duration::from_millis(250);\npub const DURATION: Duration = Duration::from_millis(500);\n\n#[derive(Debug)]\npub struct ScreenTransition {\n    /// Texture to crossfade from for each render target.\n    from_texture: [TextureBuffer<GlesTexture>; 3],\n    /// Monotonic time when to start the crossfade.\n    start_at: Duration,\n    /// Clock to drive animations.\n    clock: Clock,\n}\n\nimpl ScreenTransition {\n    pub fn new(\n        from_texture: [TextureBuffer<GlesTexture>; 3],\n        delay: Duration,\n        clock: Clock,\n    ) -> Self {\n        Self {\n            from_texture,\n            start_at: clock.now_unadjusted() + delay,\n            clock,\n        }\n    }\n\n    pub fn is_done(&self) -> bool {\n        self.start_at + DURATION <= self.clock.now_unadjusted()\n    }\n\n    pub fn update_render_elements(&mut self, scale: Scale<f64>, transform: Transform) {\n        // These textures should remain full-screen, even if scale or transform changes.\n        for buffer in &mut self.from_texture {\n            buffer.set_texture_scale(scale);\n            buffer.set_texture_transform(transform);\n        }\n    }\n\n    pub fn render(&self, target: RenderTarget) -> PrimaryGpuTextureRenderElement {\n        // Screen transition ignores animation slowdown.\n        let now = self.clock.now_unadjusted();\n\n        let alpha = if self.start_at + DURATION <= now {\n            0.\n        } else if self.start_at <= now {\n            1. - (now - self.start_at).as_secs_f32() / DURATION.as_secs_f32()\n        } else {\n            1.\n        };\n\n        let idx = match target {\n            RenderTarget::Output => 0,\n            RenderTarget::Screencast => 1,\n            RenderTarget::ScreenCapture => 2,\n        };\n\n        PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(\n            self.from_texture[idx].clone(),\n            (0., 0.),\n            alpha,\n            None,\n            None,\n            Kind::Unspecified,\n        ))\n    }\n}\n"
  },
  {
    "path": "src/ui/screenshot_ui.rs",
    "content": "use std::cell::RefCell;\nuse std::cmp::{max, min};\nuse std::collections::HashMap;\nuse std::f64::consts::TAU;\nuse std::iter::zip;\nuse std::rc::Rc;\n\nuse anyhow::Context;\nuse arrayvec::ArrayVec;\nuse niri_config::{Action, Config};\nuse niri_ipc::SizeChange;\nuse pango::{Alignment, FontDescription};\nuse pangocairo::cairo::{self, ImageSurface};\nuse smithay::backend::allocator::Fourcc;\nuse smithay::backend::input::TouchSlot;\nuse smithay::backend::renderer::element::utils::{Relocate, RelocateRenderElement};\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};\nuse smithay::backend::renderer::{ExportMem, Texture as _};\nuse smithay::input::keyboard::{Keysym, ModifiersState};\nuse smithay::output::{Output, WeakOutput};\nuse smithay::utils::{Buffer, Physical, Point, Rectangle, Scale, Size, Transform};\n\nuse crate::animation::{Animation, Clock};\nuse crate::layout::floating::DIRECTIONAL_MOVE_PX;\nuse crate::niri_render_elements;\nuse crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};\nuse crate::render_helpers::{render_to_texture, RenderTarget};\nuse crate::utils::to_physical_precise_round;\n\nconst SELECTION_BORDER: i32 = 2;\n\nconst PADDING: i32 = 8;\nconst RADIUS: i32 = 16;\nconst FONT: &str = \"sans 14px\";\nconst BORDER: i32 = 4;\nconst TEXT_HIDE_P: &str =\n    \"Press <span face='mono' bgcolor='#2C2C2C'> Space </span> to save the screenshot.\\n\\\n     Press <span face='mono' bgcolor='#2C2C2C'> P </span> to hide the pointer.\";\nconst TEXT_SHOW_P: &str =\n    \"Press <span face='mono' bgcolor='#2C2C2C'> Space </span> to save the screenshot.\\n\\\n     Press <span face='mono' bgcolor='#2C2C2C'> P </span> to show the pointer.\";\n\n// Ideally the screenshot UI should support cross-output selections. However, that poses some\n// technical challenges when the outputs have different scales and such. So, this implementation\n// allows only single-output selections for now.\n//\n// As a consequence of this, selection coordinates are in output-local coordinate space.\n#[allow(clippy::large_enum_variant)]\npub enum ScreenshotUi {\n    Closed {\n        last_selection: Option<(WeakOutput, Rectangle<i32, Physical>)>,\n        clock: Clock,\n        config: Rc<RefCell<Config>>,\n    },\n    Open {\n        selection: (Output, Point<i32, Physical>, Point<i32, Physical>),\n        output_data: HashMap<Output, OutputData>,\n        button: Button,\n        show_pointer: bool,\n        open_anim: Animation,\n        clock: Clock,\n        config: Rc<RefCell<Config>>,\n        path: Option<String>,\n    },\n}\n\n/// State for moving the selection (as opposed to just drawing).\npub struct MoveState {\n    // Cursor offset from selection.1 when starting the move.\n    pointer_offset: Point<i32, Physical>,\n    // If the move is initiated by a touch, this is the slot. If `None`, the move was initiated by\n    // holding Space.\n    touch_slot: Option<TouchSlot>,\n}\n\npub enum Button {\n    Up,\n    Down {\n        touch_slot: Option<TouchSlot>,\n        on_capture_button: bool,\n        last_pos: (Output, Point<i32, Physical>),\n        move_state: Option<MoveState>,\n    },\n}\n\npub struct OutputData {\n    size: Size<i32, Physical>,\n    scale: f64,\n    transform: Transform,\n    // Output, screencast, screen capture.\n    screenshot: [OutputScreenshot; 3],\n    buffers: [SolidColorBuffer; 8],\n    locations: [Point<i32, Physical>; 8],\n    panel: Option<(TextureBuffer<GlesTexture>, TextureBuffer<GlesTexture>)>,\n}\n\npub struct OutputScreenshot {\n    texture: GlesTexture,\n    buffer: PrimaryGpuTextureRenderElement,\n    pointer: Option<PrimaryGpuTextureRenderElement>,\n}\n\nniri_render_elements! {\n    ScreenshotUiRenderElement => {\n        Screenshot = PrimaryGpuTextureRenderElement,\n        SolidColor = SolidColorRenderElement,\n    }\n}\n\nimpl Button {\n    fn is_down(&self) -> bool {\n        matches!(self, Self::Down { .. })\n    }\n\n    fn is_dragging_selection(&self) -> bool {\n        matches!(\n            self,\n            Self::Down {\n                on_capture_button: false,\n                ..\n            }\n        )\n    }\n}\n\nimpl ScreenshotUi {\n    pub fn new(clock: Clock, config: Rc<RefCell<Config>>) -> Self {\n        Self::Closed {\n            last_selection: None,\n            clock,\n            config,\n        }\n    }\n\n    pub fn open(\n        &mut self,\n        renderer: &mut GlesRenderer,\n        // Output, screencast, screen capture.\n        screenshots: HashMap<Output, [OutputScreenshot; 3]>,\n        default_output: Output,\n        show_pointer: bool,\n        path: Option<String>,\n    ) -> bool {\n        if screenshots.is_empty() {\n            return false;\n        }\n\n        let Self::Closed {\n            last_selection,\n            clock,\n            config,\n        } = self\n        else {\n            return false;\n        };\n\n        let last_selection = last_selection\n            .take()\n            .and_then(|(weak, sel)| weak.upgrade().map(|output| (output, sel)));\n        let selection = match last_selection {\n            Some(selection) if screenshots.contains_key(&selection.0) => selection,\n            _ => {\n                let output = default_output;\n                let output_transform = output.current_transform();\n                let output_mode = output.current_mode().unwrap();\n                let size = output_transform.transform_size(output_mode.size);\n                (\n                    output,\n                    Rectangle::new(\n                        Point::from((size.w / 4, size.h / 4)),\n                        Size::from((size.w / 2, size.h / 2)),\n                    ),\n                )\n            }\n        };\n\n        let selection = (\n            selection.0,\n            selection.1.loc,\n            selection.1.loc + selection.1.size - Size::from((1, 1)),\n        );\n\n        let output_data = screenshots\n            .into_iter()\n            .map(|(output, screenshot)| {\n                let transform = output.current_transform();\n                let output_mode = output.current_mode().unwrap();\n                let size = transform.transform_size(output_mode.size);\n                let scale = output.current_scale().fractional_scale();\n                let buffers = [\n                    SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]),\n                    SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]),\n                    SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]),\n                    SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]),\n                    SolidColorBuffer::new((0., 0.), [0., 0., 0., 0.5]),\n                    SolidColorBuffer::new((0., 0.), [0., 0., 0., 0.5]),\n                    SolidColorBuffer::new((0., 0.), [0., 0., 0., 0.5]),\n                    SolidColorBuffer::new((0., 0.), [0., 0., 0., 0.5]),\n                ];\n                let locations = [Default::default(); 8];\n\n                let mut render_panel_ = |text| {\n                    render_panel(renderer, scale, text)\n                        .map_err(|err| warn!(\"error rendering help panel: {err:?}\"))\n                        .ok()\n                };\n                let panel_show = render_panel_(TEXT_SHOW_P);\n                let panel_hide = render_panel_(TEXT_HIDE_P);\n                let panel = Option::zip(panel_show, panel_hide);\n\n                let data = OutputData {\n                    size,\n                    scale,\n                    transform,\n                    screenshot,\n                    buffers,\n                    locations,\n                    panel,\n                };\n                (output, data)\n            })\n            .collect();\n\n        let open_anim = {\n            let c = config.borrow();\n            Animation::new(clock.clone(), 0., 1., 0., c.animations.screenshot_ui_open.0)\n        };\n\n        *self = Self::Open {\n            selection,\n            output_data,\n            button: Button::Up,\n            show_pointer,\n            open_anim,\n            clock: clock.clone(),\n            config: config.clone(),\n            path,\n        };\n\n        self.update_buffers();\n\n        true\n    }\n\n    pub fn close(&mut self) -> bool {\n        let Self::Open {\n            selection,\n            clock,\n            config,\n            ..\n        } = self\n        else {\n            return false;\n        };\n\n        let last_selection = Some((\n            selection.0.downgrade(),\n            rect_from_corner_points(selection.1, selection.2),\n        ));\n\n        *self = Self::Closed {\n            last_selection,\n            clock: clock.clone(),\n            config: config.clone(),\n        };\n\n        true\n    }\n\n    pub fn toggle_pointer(&mut self) {\n        if let Self::Open { show_pointer, .. } = self {\n            *show_pointer = !*show_pointer;\n        }\n    }\n\n    pub fn is_open(&self) -> bool {\n        matches!(self, ScreenshotUi::Open { .. })\n    }\n\n    pub fn set_space_down(&mut self, down: bool) {\n        if let Self::Open {\n            selection,\n            button:\n                Button::Down {\n                    move_state,\n                    last_pos,\n                    ..\n                },\n            ..\n        } = self\n        {\n            if down {\n                if move_state.is_none() {\n                    *move_state = Some(MoveState {\n                        pointer_offset: last_pos.1 - selection.1,\n                        touch_slot: None,\n                    });\n                }\n            } else {\n                // Only clear if moving with Space.\n                if let Some(MoveState {\n                    touch_slot: None, ..\n                }) = move_state\n                {\n                    *move_state = None;\n                }\n            }\n        }\n    }\n\n    pub fn move_left(&mut self) {\n        let Self::Open {\n            selection: (output, a, b),\n            output_data,\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        let data = &output_data[output];\n\n        let delta: i32 = to_physical_precise_round(data.scale, DIRECTIONAL_MOVE_PX);\n        let delta = min(delta, min(a.x, b.x));\n        a.x -= delta;\n        b.x -= delta;\n\n        self.update_buffers();\n    }\n\n    pub fn move_right(&mut self) {\n        let Self::Open {\n            selection: (output, a, b),\n            output_data,\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        let data = &output_data[output];\n\n        let delta: i32 = to_physical_precise_round(data.scale, DIRECTIONAL_MOVE_PX);\n        let delta = min(delta, data.size.w - max(a.x, b.x) - 1);\n        a.x += delta;\n        b.x += delta;\n\n        self.update_buffers();\n    }\n\n    pub fn move_up(&mut self) {\n        let Self::Open {\n            selection: (output, a, b),\n            output_data,\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        let data = &output_data[output];\n\n        let delta: i32 = to_physical_precise_round(data.scale, DIRECTIONAL_MOVE_PX);\n        let delta = min(delta, min(a.y, b.y));\n        a.y -= delta;\n        b.y -= delta;\n\n        self.update_buffers();\n    }\n\n    pub fn move_down(&mut self) {\n        let Self::Open {\n            selection: (output, a, b),\n            output_data,\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        let data = &output_data[output];\n\n        let delta: i32 = to_physical_precise_round(data.scale, DIRECTIONAL_MOVE_PX);\n        let delta = min(delta, data.size.h - max(a.y, b.y) - 1);\n        a.y += delta;\n        b.y += delta;\n\n        self.update_buffers();\n    }\n\n    /// Moves the screenshot selection to a different output.\n    ///\n    /// This preserves the relative position while keeping logical size. It is (intentionally) very\n    /// similar to how floating windows move across monitors, but with one difference: floating\n    /// windows can go partially outside the view, while the screenshot selection cannot. So, we\n    /// clamp it to new output bounds, trying to preserve the size if possible.\n    pub fn move_to_output(&mut self, new_output: Output) {\n        let Self::Open {\n            selection,\n            output_data,\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        let (current_output, current_a, current_b) = selection;\n\n        if current_output == &new_output {\n            return;\n        }\n\n        let Some(target_data) = output_data.get(&new_output) else {\n            return;\n        };\n\n        let current_data = &output_data[current_output];\n\n        let current_rect: Rectangle<_, Physical> = Rectangle::new(\n            Point::from((current_a.x.min(current_b.x), current_a.y.min(current_b.y))),\n            Size::from((\n                (current_a.x.max(current_b.x) - current_a.x.min(current_b.x) + 1),\n                (current_a.y.max(current_b.y) - current_a.y.min(current_b.y) + 1),\n            )),\n        );\n        let current_rect = current_rect.to_f64();\n\n        let rel_x = current_rect.loc.x / current_data.size.w as f64;\n        let rel_y = current_rect.loc.y / current_data.size.h as f64;\n\n        let factor = target_data.scale / current_data.scale;\n        let mut new_width = (current_rect.size.w * factor).round() as i32;\n        let mut new_height = (current_rect.size.h * factor).round() as i32;\n\n        new_width = new_width.clamp(1, target_data.size.w);\n        new_height = new_height.clamp(1, target_data.size.h);\n\n        let new_x = (rel_x * target_data.size.w as f64).round() as i32;\n        let new_y = (rel_y * target_data.size.h as f64).round() as i32;\n\n        let max_x = target_data.size.w - new_width;\n        let max_y = target_data.size.h - new_height;\n        let new_x = new_x.clamp(0, max_x);\n        let new_y = new_y.clamp(0, max_y);\n\n        let new_rect = Rectangle::new(\n            Point::from((new_x, new_y)),\n            Size::from((new_width, new_height)),\n        );\n\n        *selection = (\n            new_output,\n            new_rect.loc,\n            new_rect.loc + new_rect.size - Size::from((1, 1)),\n        );\n\n        self.update_buffers();\n    }\n\n    pub fn set_width(&mut self, change: SizeChange) {\n        let Self::Open {\n            selection: (output, a, b),\n            output_data,\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        let data = &output_data[output];\n\n        let available_size = f64::from(data.size.w);\n        let current_size = max(a.x, b.x) + 1 - min(a.x, b.x);\n\n        let new_size = match change {\n            SizeChange::SetFixed(fixed) => to_physical_precise_round(data.scale, fixed),\n            SizeChange::SetProportion(prop) => {\n                let prop = (prop / 100.).clamp(0., 1.);\n                (available_size * prop).round() as i32\n            }\n            SizeChange::AdjustFixed(delta) => {\n                let delta = to_physical_precise_round(data.scale, delta);\n                current_size.saturating_add(delta)\n            }\n            SizeChange::AdjustProportion(delta) => {\n                let current_prop = f64::from(current_size) / available_size;\n                let prop = (current_prop + delta / 100.).clamp(0., 1.);\n                (available_size * prop).round() as i32\n            }\n        };\n        let new_size = new_size.clamp(1, data.size.w - min(a.x, b.x)) - 1;\n        a.x = min(a.x, b.x);\n        b.x = a.x + new_size;\n\n        self.update_buffers();\n    }\n\n    pub fn set_height(&mut self, change: SizeChange) {\n        let Self::Open {\n            selection: (output, a, b),\n            output_data,\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        let data = &output_data[output];\n\n        let available_size = f64::from(data.size.h);\n        let current_size = max(a.y, b.y) + 1 - min(a.y, b.y);\n\n        let new_size = match change {\n            SizeChange::SetFixed(fixed) => to_physical_precise_round(data.scale, fixed),\n            SizeChange::SetProportion(prop) => {\n                let prop = (prop / 100.).clamp(0., 1.);\n                (available_size * prop).round() as i32\n            }\n            SizeChange::AdjustFixed(delta) => {\n                let delta = to_physical_precise_round(data.scale, delta);\n                current_size.saturating_add(delta)\n            }\n            SizeChange::AdjustProportion(delta) => {\n                let current_prop = f64::from(current_size) / available_size;\n                let prop = (current_prop + delta / 100.).clamp(0., 1.);\n                (available_size * prop).round() as i32\n            }\n        };\n        let new_size = new_size.clamp(1, data.size.h - min(a.y, b.y)) - 1;\n        a.y = min(a.y, b.y);\n        b.y = a.y + new_size;\n\n        self.update_buffers();\n    }\n\n    pub fn advance_animations(&mut self) {}\n\n    pub fn are_animations_ongoing(&self) -> bool {\n        let Self::Open { open_anim, .. } = self else {\n            return false;\n        };\n\n        !open_anim.is_done()\n    }\n\n    fn update_buffers(&mut self) {\n        let Self::Open {\n            selection,\n            output_data,\n            ..\n        } = self\n        else {\n            panic!(\"screenshot UI must be open to update buffers\");\n        };\n\n        let (selection_output, a, b) = selection;\n        let mut rect = rect_from_corner_points(*a, *b);\n\n        for (output, data) in output_data {\n            let buffers = &mut data.buffers;\n            let locations = &mut data.locations;\n            let size = data.size;\n            let scale = data.scale;\n\n            if output == selection_output {\n                // Check if the selection is still valid. If not, reset it back to default.\n                if !Rectangle::from_size(size).contains_rect(rect) {\n                    rect = Rectangle::new(\n                        Point::from((size.w / 4, size.h / 4)),\n                        Size::from((size.w / 2, size.h / 2)),\n                    );\n                    *a = rect.loc;\n                    *b = rect.loc + rect.size - Size::from((1, 1));\n                }\n\n                let border = to_physical_precise_round(scale, SELECTION_BORDER);\n\n                let resize = move |buffer: &mut SolidColorBuffer, w: i32, h: i32| {\n                    let size = Size::<_, Physical>::from((w, h));\n                    buffer.resize(size.to_f64().to_logical(scale));\n                };\n\n                resize(&mut buffers[0], rect.size.w + border * 2, border);\n                resize(&mut buffers[1], rect.size.w + border * 2, border);\n                resize(&mut buffers[2], border, rect.size.h);\n                resize(&mut buffers[3], border, rect.size.h);\n\n                resize(&mut buffers[4], size.w, rect.loc.y);\n                resize(&mut buffers[5], size.w, size.h - rect.loc.y - rect.size.h);\n                resize(&mut buffers[6], rect.loc.x, rect.size.h);\n                resize(\n                    &mut buffers[7],\n                    size.w - rect.loc.x - rect.size.w,\n                    rect.size.h,\n                );\n\n                locations[0] = Point::from((rect.loc.x - border, rect.loc.y - border));\n                locations[1] = Point::from((rect.loc.x - border, rect.loc.y + rect.size.h));\n                locations[2] = Point::from((rect.loc.x - border, rect.loc.y));\n                locations[3] = Point::from((rect.loc.x + rect.size.w, rect.loc.y));\n\n                locations[5] = Point::from((0, rect.loc.y + rect.size.h));\n                locations[6] = Point::from((0, rect.loc.y));\n                locations[7] = Point::from((rect.loc.x + rect.size.w, rect.loc.y));\n            } else {\n                buffers[0].resize((0., 0.));\n                buffers[1].resize((0., 0.));\n                buffers[2].resize((0., 0.));\n                buffers[3].resize((0., 0.));\n\n                buffers[4].resize(size.to_f64().to_logical(data.scale));\n                buffers[5].resize((0., 0.));\n                buffers[6].resize((0., 0.));\n                buffers[7].resize((0., 0.));\n            }\n        }\n    }\n\n    pub fn render_output(\n        &self,\n        output: &Output,\n        target: RenderTarget,\n        push: &mut dyn FnMut(ScreenshotUiRenderElement),\n    ) {\n        let _span = tracy_client::span!(\"ScreenshotUi::render_output\");\n\n        let Self::Open {\n            output_data,\n            show_pointer,\n            button,\n            open_anim,\n            ..\n        } = self\n        else {\n            panic!(\"screenshot UI must be open to render it\");\n        };\n\n        let Some(output_data) = output_data.get(output) else {\n            return;\n        };\n\n        let scale = output_data.scale;\n        let progress = open_anim.clamped_value().clamp(0., 1.) as f32;\n\n        // The help panel goes on top.\n        if let Some((show, hide)) = &output_data.panel {\n            let buffer = if *show_pointer { hide } else { show };\n            let alpha = if button.is_dragging_selection() {\n                0.3\n            } else {\n                0.9\n            };\n            let location = panel_location(output_data, buffer.texture().size())\n                .to_f64()\n                .to_logical(scale);\n\n            let elem = PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(\n                buffer.clone(),\n                location,\n                alpha * progress,\n                None,\n                None,\n                Kind::Unspecified,\n            ));\n            push(elem.into());\n        }\n\n        for (buffer, loc) in zip(&output_data.buffers, &output_data.locations) {\n            let elem = SolidColorRenderElement::from_buffer(\n                buffer,\n                loc.to_f64().to_logical(scale),\n                progress,\n                Kind::Unspecified,\n            );\n            push(elem.into());\n        }\n\n        // The screenshot itself goes last.\n        let index = match target {\n            RenderTarget::Output => 0,\n            RenderTarget::Screencast => 1,\n            RenderTarget::ScreenCapture => 2,\n        };\n        let screenshot = &output_data.screenshot[index];\n\n        if *show_pointer {\n            if let Some(pointer) = screenshot.pointer.clone() {\n                push(pointer.into());\n            }\n        }\n        push(screenshot.buffer.clone().into());\n    }\n\n    pub fn capture(\n        &self,\n        renderer: &mut GlesRenderer,\n    ) -> anyhow::Result<(Size<i32, Physical>, Vec<u8>)> {\n        let _span = tracy_client::span!(\"ScreenshotUi::capture\");\n\n        let Self::Open {\n            selection,\n            output_data,\n            show_pointer,\n            ..\n        } = self\n        else {\n            panic!(\"screenshot UI must be open to capture\");\n        };\n\n        let data = &output_data[&selection.0];\n        let rect = rect_from_corner_points(selection.1, selection.2);\n\n        let screenshot = &data.screenshot[0];\n\n        // Composite the pointer on top if needed.\n        let mut tex_rect = None;\n        if *show_pointer {\n            if let Some(pointer) = screenshot.pointer.clone() {\n                let scale = pointer.0.buffer().texture_scale();\n                let offset = rect.loc.upscale(-1);\n\n                let mut elements = ArrayVec::<_, 2>::new();\n                elements.push(pointer);\n                elements.push(screenshot.buffer.clone());\n                let elements = elements.iter().rev().map(|elem| {\n                    RelocateRenderElement::from_element(elem, offset, Relocate::Relative)\n                });\n\n                let res = render_to_texture(\n                    renderer,\n                    rect.size,\n                    scale,\n                    Transform::Normal,\n                    Fourcc::Abgr8888,\n                    elements,\n                );\n                match res {\n                    Ok((texture, _)) => {\n                        tex_rect = Some((texture, Rectangle::from_size(rect.size)));\n                    }\n                    Err(err) => {\n                        warn!(\"error compositing pointer onto screenshot: {err:?}\");\n                    }\n                }\n            }\n        }\n\n        let (texture, rect) = tex_rect.unwrap_or_else(|| (screenshot.texture.clone(), rect));\n        // The size doesn't actually matter because we're not transforming anything.\n        let buf_rect = rect\n            .to_logical(1)\n            .to_buffer(1, Transform::Normal, &Size::from((1, 1)));\n\n        let mapping = renderer\n            .copy_texture(&texture, buf_rect, Fourcc::Abgr8888)\n            .context(\"error copying texture\")?;\n        let copy = renderer\n            .map_texture(&mapping)\n            .context(\"error mapping texture\")?;\n\n        Ok((rect.size, copy.to_vec()))\n    }\n\n    pub fn action(&self, raw: Keysym, mods: ModifiersState) -> Option<Action> {\n        let Self::Open { button, .. } = self else {\n            return None;\n        };\n\n        // Pressing Space while the button is down goes into origin moving rather than capture.\n        if matches!(button, Button::Down { .. }) && raw == Keysym::space {\n            return None;\n        }\n\n        action(raw, mods)\n    }\n\n    pub fn selection_output(&self) -> Option<&Output> {\n        if let Self::Open {\n            selection: (output, _, _),\n            ..\n        } = self\n        {\n            Some(output)\n        } else {\n            None\n        }\n    }\n\n    pub fn output_size(&self, output: &Output) -> Option<(Size<i32, Physical>, f64, Transform)> {\n        if let Self::Open { output_data, .. } = self {\n            let data = output_data.get(output)?;\n            Some((data.size, data.scale, data.transform))\n        } else {\n            None\n        }\n    }\n\n    /// The pointer has moved to `point` relative to the current selection output.\n    pub fn pointer_motion(&mut self, point: Point<i32, Physical>, slot: Option<TouchSlot>) {\n        let Self::Open {\n            selection,\n            output_data,\n            button:\n                Button::Down {\n                    touch_slot,\n                    on_capture_button,\n                    last_pos,\n                    move_state,\n                },\n            ..\n        } = self\n        else {\n            return;\n        };\n\n        if *touch_slot != slot {\n            return;\n        }\n\n        last_pos.1 = point;\n\n        if *on_capture_button {\n            return;\n        }\n\n        if let Some(move_state) = move_state {\n            // The cursor offset is relative to selection.1.\n            let delta = point - (selection.1 + move_state.pointer_offset);\n\n            let desired = rect_from_corner_points(selection.1 + delta, selection.2 + delta);\n            let bounds = Rectangle::from_size(output_data[&selection.0].size - desired.size);\n            let clamped_loc = desired.loc.constrain(bounds);\n\n            let delta = clamped_loc - rect_from_corner_points(selection.1, selection.2).loc;\n            selection.1 += delta;\n            selection.2 += delta;\n        } else {\n            selection.2 = point;\n        }\n\n        self.update_buffers();\n    }\n\n    pub fn pointer_down(\n        &mut self,\n        output: Output,\n        point: Point<i32, Physical>,\n        slot: Option<TouchSlot>,\n    ) -> bool {\n        let Self::Open {\n            selection,\n            output_data,\n            show_pointer,\n            button,\n            ..\n        } = self\n        else {\n            return false;\n        };\n\n        // Check if this is a second touch (different slot) while already dragging.\n        if let Some(new_slot) = slot {\n            if let Button::Down {\n                on_capture_button: false,\n                move_state,\n                last_pos,\n                ..\n            } = button\n            {\n                if move_state.is_none() {\n                    *move_state = Some(MoveState {\n                        pointer_offset: last_pos.1 - selection.1,\n                        touch_slot: Some(new_slot),\n                    });\n                }\n            }\n        }\n\n        if button.is_down() {\n            return false;\n        }\n\n        let Some(output_data) = output_data.get(&output) else {\n            return false;\n        };\n\n        if let Some((show, hide)) = &output_data.panel {\n            let buffer = if *show_pointer { hide } else { show };\n            let panel_size = buffer.texture().size();\n            let location = panel_location(output_data, panel_size);\n\n            if is_within_capture_button(output_data.scale, panel_size, point - location) {\n                *button = Button::Down {\n                    touch_slot: slot,\n                    on_capture_button: true,\n                    last_pos: (output, point),\n                    move_state: None,\n                };\n                return false;\n            }\n        }\n\n        *button = Button::Down {\n            touch_slot: slot,\n            on_capture_button: false,\n            last_pos: (output.clone(), point),\n            move_state: None,\n        };\n        *selection = (output, point, point);\n\n        self.update_buffers();\n\n        true\n    }\n\n    pub fn pointer_up(&mut self, slot: Option<TouchSlot>) -> Option<bool> {\n        let Self::Open {\n            selection,\n            output_data,\n            button,\n            show_pointer,\n            ..\n        } = self\n        else {\n            return None;\n        };\n\n        let Button::Down {\n            touch_slot,\n            on_capture_button,\n            ref last_pos,\n            ref mut move_state,\n            ..\n        } = *button\n        else {\n            return None;\n        };\n\n        // Check if this is a move touch and if so, stop the move.\n        if let Some(state) = move_state {\n            if state.touch_slot.is_some_and(|m_slot| Some(m_slot) == slot) {\n                *move_state = None;\n                return None;\n            }\n        };\n\n        if touch_slot != slot {\n            return None;\n        }\n\n        let last_pos = last_pos.clone();\n        *button = Button::Up;\n\n        // Check if we released still on the capture button.\n        if on_capture_button {\n            let (output, point) = last_pos;\n\n            #[allow(clippy::question_mark)]\n            let Some(output_data) = output_data.get(&output) else {\n                return None;\n            };\n\n            if let Some((show, hide)) = &output_data.panel {\n                let buffer = if *show_pointer { hide } else { show };\n                let panel_size = buffer.texture().size();\n                let location = panel_location(output_data, panel_size);\n\n                if is_within_capture_button(output_data.scale, panel_size, point - location) {\n                    return Some(true);\n                }\n            }\n        }\n\n        // Check if the resulting selection is zero-sized, and try to come up with a small\n        // default rectangle.\n        let (output, a, b) = selection;\n        let mut rect = rect_from_corner_points(*a, *b);\n        if rect.size.is_empty() || rect.size == Size::from((1, 1)) {\n            let data = &output_data[output];\n            rect = Rectangle::new(\n                Point::from((rect.loc.x - 16, rect.loc.y - 16)),\n                Size::from((32, 32)),\n            )\n            .intersection(Rectangle::from_size(data.size))\n            .unwrap_or_default();\n            *a = rect.loc;\n            *b = rect.loc + rect.size - Size::from((1, 1));\n        }\n\n        self.update_buffers();\n\n        Some(false)\n    }\n}\n\nimpl OutputScreenshot {\n    pub fn from_textures(\n        renderer: &mut GlesRenderer,\n        scale: Scale<f64>,\n        texture: GlesTexture,\n        pointer: Option<(GlesTexture, Rectangle<i32, Physical>)>,\n    ) -> Self {\n        let buffer = PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(\n            TextureBuffer::from_texture(\n                renderer,\n                texture.clone(),\n                scale,\n                Transform::Normal,\n                Vec::new(),\n            ),\n            (0., 0.),\n            1.,\n            None,\n            None,\n            Kind::Unspecified,\n        ));\n\n        let pointer = pointer.map(|(texture, geo)| {\n            PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(\n                TextureBuffer::from_texture(\n                    renderer,\n                    texture,\n                    scale,\n                    Transform::Normal,\n                    Vec::new(),\n                ),\n                geo.to_f64().to_logical(scale).loc,\n                1.,\n                None,\n                None,\n                Kind::Unspecified,\n            ))\n        });\n\n        Self {\n            texture,\n            buffer,\n            pointer,\n        }\n    }\n}\n\nfn action(raw: Keysym, mods: ModifiersState) -> Option<Action> {\n    if raw == Keysym::Escape {\n        return Some(Action::CancelScreenshot);\n    }\n\n    if mods.alt || mods.shift {\n        return None;\n    }\n\n    if !mods.ctrl && (raw == Keysym::space || raw == Keysym::Return) {\n        return Some(Action::ConfirmScreenshot {\n            write_to_disk: true,\n        });\n    }\n    if mods.ctrl && raw == Keysym::c {\n        return Some(Action::ConfirmScreenshot {\n            write_to_disk: false,\n        });\n    }\n\n    if !mods.ctrl && raw == Keysym::p {\n        return Some(Action::ScreenshotTogglePointer);\n    }\n\n    None\n}\n\npub fn rect_from_corner_points(\n    a: Point<i32, Physical>,\n    b: Point<i32, Physical>,\n) -> Rectangle<i32, Physical> {\n    let x1 = min(a.x, b.x);\n    let y1 = min(a.y, b.y);\n    let x2 = max(a.x, b.x);\n    let y2 = max(a.y, b.y);\n    // We're adding + 1 because the pointer is clamped to output size - 1, so to get the full\n    // screen worth of selection we must add back that + 1.\n    Rectangle::from_extremities((x1, y1), (x2 + 1, y2 + 1))\n}\n\nfn panel_location(output_data: &OutputData, panel_size: Size<i32, Buffer>) -> Point<i32, Physical> {\n    let scale = output_data.scale;\n    let padding: i32 = to_physical_precise_round(scale, PADDING);\n    let x = max(0, (output_data.size.w - panel_size.w) / 2);\n    let y = max(0, output_data.size.h - panel_size.h - padding * 2);\n    Point::from((x, y))\n}\n\nfn is_within_capture_button(\n    scale: f64,\n    panel_size: Size<i32, Buffer>,\n    pos_within_panel: Point<i32, Physical>,\n) -> bool {\n    let padding: i32 = to_physical_precise_round(scale, PADDING);\n    let radius = to_physical_precise_round::<i32>(scale, RADIUS) - 2;\n\n    let xc = padding + radius;\n    let yc = panel_size.h / 2;\n    let pos = pos_within_panel;\n\n    (pos.x - xc) * (pos.x - xc) + (pos.y - yc) * (pos.y - yc) <= radius * radius\n}\n\nfn render_panel(\n    renderer: &mut GlesRenderer,\n    scale: f64,\n    text: &str,\n) -> anyhow::Result<TextureBuffer<GlesTexture>> {\n    let _span = tracy_client::span!(\"screenshot_ui::render_panel\");\n\n    let padding: i32 = to_physical_precise_round(scale, PADDING);\n    // Keep the border width even to avoid blurry edges.\n    let border_width = (f64::from(BORDER) / 2. * scale).round() * 2.;\n    let half_border_width = (border_width / 2.) as i32;\n    let radius: i32 = to_physical_precise_round(scale, RADIUS);\n    let circle_stroke: f64 = to_physical_precise_round(scale, 2.);\n\n    // Add 2 px of spacing to separate the backgrounds of the \"Space\" and \"P\" keys.\n    let spacing = to_physical_precise_round::<i32>(scale, 2) * 1024;\n\n    let mut font = FontDescription::from_string(FONT);\n    font.set_absolute_size(to_physical_precise_round(scale, font.size()));\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, 0, 0)?;\n    let cr = cairo::Context::new(&surface)?;\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_alignment(Alignment::Left);\n    layout.set_markup(text);\n    layout.set_spacing(spacing);\n\n    let (mut width, mut height) = layout.pixel_size();\n\n    width += padding + radius * 2 + padding - half_border_width + padding;\n    height = max(height, radius * 2);\n    height += padding * 2;\n\n    let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;\n    let cr = cairo::Context::new(&surface)?;\n    cr.set_source_rgb(0.1, 0.1, 0.1);\n    cr.paint()?;\n\n    let padding = f64::from(padding);\n    let half_border_width = f64::from(half_border_width);\n    let r = f64::from(radius);\n\n    let yc = f64::from(height / 2);\n\n    cr.new_sub_path();\n    cr.arc(padding + r, yc, r, 0., TAU);\n    cr.set_source_rgb(1., 1., 1.);\n    cr.fill()?;\n\n    cr.new_sub_path();\n    cr.arc(padding + r, yc, r - circle_stroke, 0., TAU);\n    cr.set_source_rgb(0.1, 0.1, 0.1);\n    cr.fill()?;\n\n    cr.new_sub_path();\n    cr.arc(padding + r, yc, r - circle_stroke * 2., 0., TAU);\n    cr.set_source_rgb(1., 1., 1.);\n    cr.fill()?;\n\n    cr.move_to(padding + r * 2. + padding - half_border_width, padding);\n\n    let layout = pangocairo::functions::create_layout(&cr);\n    layout.context().set_round_glyph_positions(false);\n    layout.set_font_description(Some(&font));\n    layout.set_alignment(Alignment::Left);\n    layout.set_markup(text);\n    layout.set_spacing(spacing);\n\n    cr.set_source_rgb(1., 1., 1.);\n    pangocairo::functions::show_layout(&cr, &layout);\n\n    cr.move_to(0., 0.);\n    cr.line_to(width.into(), 0.);\n    cr.line_to(width.into(), height.into());\n    cr.line_to(0., height.into());\n    cr.line_to(0., 0.);\n    cr.set_source_rgb(0.3, 0.3, 0.3);\n    cr.set_line_width(border_width);\n    cr.stroke()?;\n    drop(cr);\n\n    let data = surface.take_data().unwrap();\n    let buffer = TextureBuffer::from_memory(\n        renderer,\n        &data,\n        Fourcc::Argb8888,\n        (width, height),\n        false,\n        scale,\n        Transform::Normal,\n        Vec::new(),\n    )?;\n\n    Ok(buffer)\n}\n"
  },
  {
    "path": "src/utils/id.rs",
    "content": "use std::sync::atomic::{AtomicU64, Ordering};\n\n/// Counter that returns unique IDs.\npub struct IdCounter {\n    value: AtomicU64,\n}\n\nimpl IdCounter {\n    pub const fn new() -> Self {\n        Self {\n            // Start from 1 to reduce the possibility that some other code that uses these IDs will\n            // get confused.\n            value: AtomicU64::new(1),\n        }\n    }\n\n    pub fn next(&self) -> u64 {\n        self.value.fetch_add(1, Ordering::Relaxed)\n    }\n}\n\nimpl Default for IdCounter {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "src/utils/mod.rs",
    "content": "use std::cmp::{max, min};\nuse std::ffi::{CString, OsStr};\nuse std::fmt::Display;\nuse std::io::Write;\nuse std::os::unix::prelude::OsStrExt;\nuse std::path::{Path, PathBuf};\nuse std::ptr::null_mut;\nuse std::sync::atomic::AtomicBool;\nuse std::time::Duration;\nuse std::{f64, fmt};\n\nuse anyhow::{ensure, Context};\nuse bitflags::bitflags;\nuse directories::UserDirs;\nuse git_version::git_version;\nuse niri_config::{Config, OutputName};\nuse smithay::backend::renderer::utils::with_renderer_surface_state;\nuse smithay::input::pointer::CursorIcon;\nuse smithay::output::{self, Output};\nuse smithay::reexports::rustix::time::{clock_gettime, ClockId};\nuse smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::reexports::wayland_server::{Client, DisplayHandle, Resource as _};\nuse smithay::utils::{Coordinate, Logical, Point, Rectangle, Size, Transform};\nuse smithay::wayland::compositor::{send_surface_state, with_states, SurfaceData};\nuse smithay::wayland::fractional_scale::with_fractional_scale;\nuse smithay::wayland::shell::xdg::{\n    ToplevelCachedState, ToplevelConfigure, ToplevelState, ToplevelSurface, XdgToplevelSurfaceData,\n    XdgToplevelSurfaceRoleAttributes,\n};\nuse wayland_backend::server::Credentials;\n\nuse crate::handlers::KdeDecorationsModeState;\nuse crate::niri::ClientState;\n\npub mod id;\npub mod scale;\npub mod signals;\npub mod spawning;\npub mod transaction;\npub mod vblank_throttle;\npub mod watcher;\npub mod xwayland;\n\npub static IS_SYSTEMD_SERVICE: AtomicBool = AtomicBool::new(false);\n\nuse id::IdCounter;\n\n/// Unique ID for a screencast session.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct CastSessionId(u64);\n\nimpl CastSessionId {\n    pub fn next() -> Self {\n        static COUNTER: IdCounter = IdCounter::new();\n        Self(COUNTER.next())\n    }\n\n    pub fn get(self) -> u64 {\n        self.0\n    }\n}\n\nimpl Display for CastSessionId {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\nimpl From<u64> for CastSessionId {\n    fn from(value: u64) -> Self {\n        Self(value)\n    }\n}\n\n/// Unique ID for a screencast stream.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct CastStreamId(u64);\n\nimpl CastStreamId {\n    pub fn next() -> Self {\n        static COUNTER: IdCounter = IdCounter::new();\n        Self(COUNTER.next())\n    }\n\n    pub fn get(self) -> u64 {\n        self.0\n    }\n}\n\nimpl Display for CastStreamId {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\nbitflags! {\n    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n    pub struct ResizeEdge: u32 {\n        const TOP          = 0b0001;\n        const BOTTOM       = 0b0010;\n        const LEFT         = 0b0100;\n        const RIGHT        = 0b1000;\n\n        const TOP_LEFT     = Self::TOP.bits() | Self::LEFT.bits();\n        const BOTTOM_LEFT  = Self::BOTTOM.bits() | Self::LEFT.bits();\n\n        const TOP_RIGHT    = Self::TOP.bits() | Self::RIGHT.bits();\n        const BOTTOM_RIGHT = Self::BOTTOM.bits() | Self::RIGHT.bits();\n\n        const LEFT_RIGHT   = Self::LEFT.bits() | Self::RIGHT.bits();\n        const TOP_BOTTOM   = Self::TOP.bits() | Self::BOTTOM.bits();\n    }\n}\n\nimpl From<xdg_toplevel::ResizeEdge> for ResizeEdge {\n    #[inline]\n    fn from(x: xdg_toplevel::ResizeEdge) -> Self {\n        Self::from_bits(x as u32).unwrap()\n    }\n}\n\nimpl ResizeEdge {\n    pub fn cursor_icon(self) -> CursorIcon {\n        match self {\n            Self::LEFT => CursorIcon::WResize,\n            Self::RIGHT => CursorIcon::EResize,\n            Self::TOP => CursorIcon::NResize,\n            Self::BOTTOM => CursorIcon::SResize,\n            Self::TOP_LEFT => CursorIcon::NwResize,\n            Self::TOP_RIGHT => CursorIcon::NeResize,\n            Self::BOTTOM_RIGHT => CursorIcon::SeResize,\n            Self::BOTTOM_LEFT => CursorIcon::SwResize,\n            _ => CursorIcon::Default,\n        }\n    }\n}\n\npub fn version() -> String {\n    if let Some(v) = option_env!(\"NIRI_BUILD_VERSION_STRING\") {\n        return String::from(v);\n    }\n\n    const MAJOR: &str = env!(\"CARGO_PKG_VERSION_MAJOR\");\n    const MINOR: &str = env!(\"CARGO_PKG_VERSION_MINOR\");\n    const PATCH: &str = env!(\"CARGO_PKG_VERSION_PATCH\");\n\n    let commit =\n        option_env!(\"NIRI_BUILD_COMMIT\").unwrap_or(git_version!(fallback = \"unknown commit\"));\n\n    if PATCH == \"0\" {\n        format!(\"{MAJOR}.{MINOR:0>2} ({commit})\")\n    } else {\n        format!(\"{MAJOR}.{MINOR:0>2}.{PATCH} ({commit})\")\n    }\n}\n\npub fn get_monotonic_time() -> Duration {\n    let ts = clock_gettime(ClockId::Monotonic);\n    Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)\n}\n\npub fn center(rect: Rectangle<i32, Logical>) -> Point<i32, Logical> {\n    rect.loc + rect.size.downscale(2).to_point()\n}\n\npub fn center_f64(rect: Rectangle<f64, Logical>) -> Point<f64, Logical> {\n    rect.loc + rect.size.downscale(2.0).to_point()\n}\n\n/// Convert logical pixels to physical, rounding to physical pixels.\npub fn to_physical_precise_round<N: Coordinate>(scale: f64, logical: impl Coordinate) -> N {\n    N::from_f64((logical.to_f64() * scale).round())\n}\n\npub fn round_logical_in_physical(scale: f64, logical: f64) -> f64 {\n    (logical * scale).round() / scale\n}\n\npub fn round_logical_in_physical_max1(scale: f64, logical: f64) -> f64 {\n    if logical == 0. {\n        return 0.;\n    }\n\n    (logical * scale).max(1.).round() / scale\n}\n\npub fn floor_logical_in_physical_max1(scale: f64, logical: f64) -> f64 {\n    if logical == 0. {\n        return 0.;\n    }\n\n    (logical * scale).max(1.).floor() / scale\n}\n\npub fn output_size(output: &Output) -> Size<f64, Logical> {\n    let output_scale = output.current_scale().fractional_scale();\n    let output_transform = output.current_transform();\n    let output_mode = output.current_mode().unwrap();\n    let logical_size = output_mode.size.to_f64().to_logical(output_scale);\n    output_transform.transform_size(logical_size)\n}\n\npub fn logical_output(output: &Output) -> niri_ipc::LogicalOutput {\n    let loc = output.current_location();\n    let size = output_size(output);\n    let transform = match output.current_transform() {\n        Transform::Normal => niri_ipc::Transform::Normal,\n        Transform::_90 => niri_ipc::Transform::_90,\n        Transform::_180 => niri_ipc::Transform::_180,\n        Transform::_270 => niri_ipc::Transform::_270,\n        Transform::Flipped => niri_ipc::Transform::Flipped,\n        Transform::Flipped90 => niri_ipc::Transform::Flipped90,\n        Transform::Flipped180 => niri_ipc::Transform::Flipped180,\n        Transform::Flipped270 => niri_ipc::Transform::Flipped270,\n    };\n    niri_ipc::LogicalOutput {\n        x: loc.x,\n        y: loc.y,\n        width: size.w as u32,\n        height: size.h as u32,\n        scale: output.current_scale().fractional_scale(),\n        transform,\n    }\n}\n\npub struct PanelOrientation(pub Transform);\npub fn panel_orientation(output: &Output) -> Transform {\n    output\n        .user_data()\n        .get::<PanelOrientation>()\n        .map(|x| x.0)\n        .unwrap_or(Transform::Normal)\n}\n\npub fn ipc_transform_to_smithay(transform: niri_ipc::Transform) -> Transform {\n    match transform {\n        niri_ipc::Transform::Normal => Transform::Normal,\n        niri_ipc::Transform::_90 => Transform::_90,\n        niri_ipc::Transform::_180 => Transform::_180,\n        niri_ipc::Transform::_270 => Transform::_270,\n        niri_ipc::Transform::Flipped => Transform::Flipped,\n        niri_ipc::Transform::Flipped90 => Transform::Flipped90,\n        niri_ipc::Transform::Flipped180 => Transform::Flipped180,\n        niri_ipc::Transform::Flipped270 => Transform::Flipped270,\n    }\n}\n\npub fn is_mapped(surface: &WlSurface) -> bool {\n    // None if the surface hadn't committed yet.\n    with_renderer_surface_state(surface, |state| state.buffer().is_some()).unwrap_or(false)\n}\n\npub fn send_scale_transform(\n    surface: &WlSurface,\n    data: &SurfaceData,\n    scale: output::Scale,\n    transform: Transform,\n) {\n    send_surface_state(surface, data, scale.integer_scale(), transform);\n    with_fractional_scale(data, |fractional| {\n        fractional.set_preferred_scale(scale.fractional_scale());\n    });\n}\n\npub fn expand_home(path: &Path) -> anyhow::Result<Option<PathBuf>> {\n    if let Ok(rest) = path.strip_prefix(\"~\") {\n        let dirs = UserDirs::new().context(\"error retrieving home directory\")?;\n        Ok(Some([dirs.home_dir(), rest].iter().collect()))\n    } else {\n        Ok(None)\n    }\n}\n\npub fn make_screenshot_path(config: &Config) -> anyhow::Result<Option<PathBuf>> {\n    let Some(path) = &config.screenshot_path.0 else {\n        return Ok(None);\n    };\n\n    let format = CString::new(path.clone()).context(\"path must not contain nul bytes\")?;\n\n    let mut buf = [0u8; 2048];\n    let mut path;\n    unsafe {\n        let time = libc::time(null_mut());\n        ensure!(time != -1, \"error in time()\");\n\n        let tm = libc::localtime(&time);\n        ensure!(!tm.is_null(), \"error in localtime()\");\n\n        let rv = libc::strftime(buf.as_mut_ptr().cast(), buf.len(), format.as_ptr(), tm);\n        ensure!(rv != 0, \"error formatting time\");\n\n        path = PathBuf::from(OsStr::from_bytes(&buf[..rv]));\n    }\n\n    if let Some(expanded) = expand_home(&path).context(\"error expanding ~\")? {\n        path = expanded;\n    }\n\n    Ok(Some(path))\n}\n\npub fn write_png_rgba8(\n    w: impl Write,\n    width: u32,\n    height: u32,\n    pixels: &[u8],\n) -> Result<(), png::EncodingError> {\n    let mut encoder = png::Encoder::new(w, width, height);\n    encoder.set_color(png::ColorType::Rgba);\n    encoder.set_depth(png::BitDepth::Eight);\n\n    let mut writer = encoder.write_header()?;\n    writer.write_image_data(pixels)\n}\n\npub fn output_matches_name(output: &Output, target: &str) -> bool {\n    let name = output.user_data().get::<OutputName>().unwrap();\n    name.matches(target)\n}\n\npub fn is_laptop_panel(connector: &str) -> bool {\n    matches!(connector.get(..4), Some(\"eDP-\" | \"LVDS\" | \"DSI-\"))\n}\n\npub fn with_toplevel_role<T>(\n    toplevel: &ToplevelSurface,\n    f: impl FnOnce(&mut XdgToplevelSurfaceRoleAttributes) -> T,\n) -> T {\n    with_states(toplevel.wl_surface(), |states| {\n        let mut role = states\n            .data_map\n            .get::<XdgToplevelSurfaceData>()\n            .unwrap()\n            .lock()\n            .unwrap();\n\n        f(&mut role)\n    })\n}\n\npub fn with_toplevel_role_and_current<T>(\n    toplevel: &ToplevelSurface,\n    f: impl FnOnce(&mut XdgToplevelSurfaceRoleAttributes, Option<&ToplevelState>) -> T,\n) -> T {\n    with_states(toplevel.wl_surface(), |states| {\n        let mut role = states\n            .data_map\n            .get::<XdgToplevelSurfaceData>()\n            .unwrap()\n            .lock()\n            .unwrap();\n\n        let mut guard = states.cached_state.get::<ToplevelCachedState>();\n        let current = guard.current().last_acked.as_ref().map(|c| &c.state);\n\n        f(&mut role, current)\n    })\n}\n\npub fn with_toplevel_last_uncommitted_configure<T>(\n    toplevel: &ToplevelSurface,\n    f: impl FnOnce(Option<&ToplevelConfigure>) -> T,\n) -> T {\n    with_states(toplevel.wl_surface(), |states| {\n        let role = states\n            .data_map\n            .get::<XdgToplevelSurfaceData>()\n            .unwrap()\n            .lock()\n            .unwrap();\n\n        let mut guard = states.cached_state.get::<ToplevelCachedState>();\n\n        if let Some(last_pending) = role.pending_configures().last() {\n            // Configure not yet acked by the client.\n            f(Some(last_pending))\n        } else if let Some(last_acked) = &role.last_acked {\n            let mut configure = Some(last_acked);\n\n            if let Some(committed) = &guard.current().last_acked {\n                if committed.serial.is_no_older_than(&last_acked.serial) {\n                    // Already committed to this configure.\n                    configure = None;\n                }\n            }\n\n            f(configure)\n        } else {\n            // Surface hadn't been configured yet.\n            f(None)\n        }\n    })\n}\n\npub fn update_tiled_state(\n    toplevel: &ToplevelSurface,\n    prefer_no_csd: bool,\n    force_tiled: Option<bool>,\n) {\n    // Determine the default value for our tiled state. The idea is to use the tiled state to\n    // make windows rectangular even if they don't support xdg-decoration (e.g. GTK).\n    //\n    // If the user prefers no CSD, it's a reasonable assumption that they would prefer to get\n    // rid of the various client-side rounded corners also by using the tiled state.\n    let should_tile = || {\n        // Figure out if the client bound any decoration globals for this window. In this case,\n        // the pending decoration mode will be set to something (we always set it upon binding the\n        // global and never reset to None).\n        //\n        // If the client bound a decoration global, use the mode that we negotiated. This way,\n        // changing the decoration mode on the client at runtime will synchronize with the\n        // default tiled state.\n        if let Some(mode) = toplevel.with_pending_state(|state| state.decoration_mode) {\n            mode == zxdg_toplevel_decoration_v1::Mode::ServerSide\n        } else if let Some(mode) = with_states(toplevel.wl_surface(), |states| {\n            states.data_map.get::<KdeDecorationsModeState>().cloned()\n        }) {\n            // Actually, make the KDE decoration overridable with prefer_no_csd. GTK 3 likes to\n            // always request CSD through it, and we want prefer_no_csd to set the tiled state\n            // automatically for GTK 3. Also, unlike xdg-decoration, KDE decoration is not\n            // synchronized to commits, so that argument is less important.\n            mode.is_server() || prefer_no_csd\n        } else {\n            // The client doesn't see or doesn't care about the decoration protocols. In this\n            // case, use the current prefer_no_csd value as the user's intention.\n            //\n            // This is a bit weird because it makes it seem like prefer_no_csd can apply live,\n            // while that isn't really the case. That's because prefer_no_csd controls two separate\n            // things: whether the client sees the decoration globals, and the tiled state.\n            //\n            // A more accurate way would perhaps be to check if the client cannot see the\n            // decoration globals, and in this case behave as if prefer_no_csd was false. However,\n            // this also regresses the common case of GTK 4 applications that do not react to\n            // xdg-decoration in any way, and therefore the tiled state *is* the \"no CSD\" mode from\n            // the user's perspective, so by artificially gating it we would artificially make it\n            // impossible to apply it live for GTK 4 applications.\n            prefer_no_csd\n        }\n    };\n\n    let should_tile = force_tiled.unwrap_or_else(should_tile);\n\n    toplevel.with_pending_state(|state| {\n        if should_tile {\n            state.states.set(xdg_toplevel::State::TiledLeft);\n            state.states.set(xdg_toplevel::State::TiledRight);\n            state.states.set(xdg_toplevel::State::TiledTop);\n            state.states.set(xdg_toplevel::State::TiledBottom);\n        } else {\n            state.states.unset(xdg_toplevel::State::TiledLeft);\n            state.states.unset(xdg_toplevel::State::TiledRight);\n            state.states.unset(xdg_toplevel::State::TiledTop);\n            state.states.unset(xdg_toplevel::State::TiledBottom);\n        }\n    });\n}\n\npub fn get_credentials_for_surface(surface: &WlSurface) -> Option<Credentials> {\n    let handle = surface.handle().upgrade()?;\n    let dh = DisplayHandle::from(handle);\n\n    let client = dh.get_client(surface.id()).ok()?;\n    get_credentials_for_client(&dh, &client)\n}\n\npub fn get_credentials_for_client(dh: &DisplayHandle, client: &Client) -> Option<Credentials> {\n    let data = client.get_data::<ClientState>().unwrap();\n    if data.credentials_unknown {\n        return None;\n    }\n\n    client.get_credentials(dh).ok()\n}\n\npub fn ensure_min_max_size(mut x: i32, min_size: i32, max_size: i32) -> i32 {\n    if max_size > 0 {\n        x = min(x, max_size);\n    }\n    if min_size > 0 {\n        x = max(x, min_size);\n    }\n    x\n}\n\npub fn ensure_min_max_size_maybe_zero(x: i32, min_size: i32, max_size: i32) -> i32 {\n    if x != 0 {\n        ensure_min_max_size(x, min_size, max_size)\n    } else if min_size > 0 && min_size == max_size {\n        min_size\n    } else {\n        0\n    }\n}\n\npub fn clamp_preferring_top_left_in_area(\n    area: Rectangle<f64, Logical>,\n    rect: &mut Rectangle<f64, Logical>,\n) {\n    rect.loc.x = f64::min(rect.loc.x, area.loc.x + area.size.w - rect.size.w);\n    rect.loc.y = f64::min(rect.loc.y, area.loc.y + area.size.h - rect.size.h);\n\n    // Clamp by top and left last so it takes precedence.\n    rect.loc.x = f64::max(rect.loc.x, area.loc.x);\n    rect.loc.y = f64::max(rect.loc.y, area.loc.y);\n}\n\npub fn center_preferring_top_left_in_area(\n    area: Rectangle<f64, Logical>,\n    size: Size<f64, Logical>,\n) -> Point<f64, Logical> {\n    let area_size = area.size.to_point();\n    let size = size.to_point();\n    let mut offset = (area_size - size).downscale(2.);\n    offset.x = f64::max(offset.x, 0.);\n    offset.y = f64::max(offset.y, 0.);\n    area.loc + offset\n}\n\npub fn baba_is_float_offset(now: Duration, view_height: f64) -> f64 {\n    let now = now.as_secs_f64();\n    let amplitude = view_height / 96.;\n    amplitude * ((f64::consts::TAU * now / 3.6).sin() - 1.)\n}\n\n#[cfg(feature = \"dbus\")]\npub fn show_screenshot_notification(image_path: Option<&Path>) -> anyhow::Result<()> {\n    use std::collections::HashMap;\n\n    use pango::glib;\n    use zbus::zvariant;\n\n    let conn = zbus::blocking::Connection::session()?;\n\n    // Try to add the screenshot as an image if possible.\n    let mut image_url = None;\n    if let Some(path) = image_path {\n        match path.canonicalize() {\n            Ok(path) => match glib::filename_to_uri(path, None) {\n                Ok(url) => {\n                    image_url = Some(url);\n                }\n                Err(err) => {\n                    warn!(\"error converting screenshot path to file url: {err:?}\");\n                }\n            },\n            Err(err) => {\n                warn!(\"error canonicalizing screenshot path: {err:?}\");\n            }\n        }\n    }\n\n    let actions: &[&str] = &[];\n\n    conn.call_method(\n        Some(\"org.freedesktop.Notifications\"),\n        \"/org/freedesktop/Notifications\",\n        Some(\"org.freedesktop.Notifications\"),\n        \"Notify\",\n        &(\n            \"niri\",\n            0u32,\n            image_url.as_ref().map(|url| url.as_str()).unwrap_or(\"\"),\n            \"Screenshot captured\",\n            \"You can paste the image from the clipboard.\",\n            actions,\n            HashMap::from([\n                (\"transient\", zvariant::Value::Bool(true)),\n                (\"urgency\", zvariant::Value::U8(1)),\n            ]),\n            -1,\n        ),\n    )?;\n\n    Ok(())\n}\n\n#[inline(never)]\npub fn cause_panic() {\n    let a = Duration::from_secs(1);\n    let b = Duration::from_secs(2);\n    let _ = a - b;\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_clamp_preferring_top_left() {\n        fn check(\n            (ax, ay, aw, ah): (i32, i32, i32, i32),\n            (rx, ry, rw, rh): (i32, i32, i32, i32),\n            (ex, ey): (i32, i32),\n        ) {\n            let area = Rectangle::new(Point::from((ax, ay)), Size::from((aw, ah))).to_f64();\n            let mut rect = Rectangle::new(Point::from((rx, ry)), Size::from((rw, rh))).to_f64();\n            clamp_preferring_top_left_in_area(area, &mut rect);\n            assert_eq!(rect.loc, Point::from((ex, ey)).to_f64());\n        }\n\n        check((0, 0, 10, 20), (2, 3, 4, 5), (2, 3));\n        check((0, 0, 10, 20), (-2, 3, 4, 5), (0, 3));\n        check((0, 0, 10, 20), (2, -3, 4, 5), (2, 0));\n        check((0, 0, 10, 20), (-2, -3, 4, 5), (0, 0));\n\n        check((1, 1, 10, 20), (2, 3, 4, 5), (2, 3));\n        check((1, 1, 10, 20), (-2, 3, 4, 5), (1, 3));\n        check((1, 1, 10, 20), (2, -3, 4, 5), (2, 1));\n        check((1, 1, 10, 20), (-2, -3, 4, 5), (1, 1));\n\n        check((0, 0, 10, 20), (20, 3, 4, 5), (6, 3));\n        check((0, 0, 10, 20), (2, 30, 4, 5), (2, 15));\n        check((0, 0, 10, 20), (20, 30, 4, 5), (6, 15));\n\n        check((0, 0, 10, 20), (20, 30, 40, 5), (0, 15));\n        check((0, 0, 10, 20), (20, 30, 4, 50), (6, 0));\n        check((0, 0, 10, 20), (20, 30, 40, 50), (0, 0));\n    }\n}\n"
  },
  {
    "path": "src/utils/scale.rs",
    "content": "//! Default monitor scale calculation.\n//!\n//! This module follows logic and tests from Mutter:\n//! <https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-46/src/backends/meta-monitor.c>\n\nuse smithay::utils::{Physical, Raw, Size};\n\nconst MIN_SCALE: i32 = 1;\nconst MAX_SCALE: i32 = 4;\nconst STEPS: i32 = 4;\nconst MIN_LOGICAL_AREA: i32 = 800 * 480;\n\nconst MOBILE_TARGET_DPI: f64 = 135.;\nconst LARGE_TARGET_DPI: f64 = 110.;\nconst LARGE_MIN_SIZE_INCHES: f64 = 20.;\n\n/// Calculates the ideal scale for a monitor.\npub fn guess_monitor_scale(size_mm: Size<i32, Raw>, resolution: Size<i32, Physical>) -> f64 {\n    if size_mm.w == 0 || size_mm.h == 0 {\n        return 1.;\n    }\n\n    let diag_inches = f64::from(size_mm.w * size_mm.w + size_mm.h * size_mm.h).sqrt() / 25.4;\n\n    let target_dpi = if diag_inches < LARGE_MIN_SIZE_INCHES {\n        MOBILE_TARGET_DPI\n    } else {\n        LARGE_TARGET_DPI\n    };\n\n    let physical_dpi =\n        f64::from(resolution.w * resolution.w + resolution.h * resolution.h).sqrt() / diag_inches;\n    let perfect_scale = physical_dpi / target_dpi;\n\n    supported_scales(resolution)\n        .map(|scale| (scale, (scale - perfect_scale).abs()))\n        .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap())\n        .map_or(1., |(scale, _)| scale)\n}\n\npub fn supported_scales(resolution: Size<i32, Physical>) -> impl Iterator<Item = f64> {\n    (MIN_SCALE * STEPS..=MAX_SCALE * STEPS)\n        .map(|x| f64::from(x) / f64::from(STEPS))\n        .filter(move |scale| is_valid_for_resolution(resolution, *scale))\n}\n\nfn is_valid_for_resolution(resolution: Size<i32, Physical>, scale: f64) -> bool {\n    let logical = resolution.to_f64().to_logical(scale).to_i32_round::<i32>();\n    logical.w * logical.h >= MIN_LOGICAL_AREA\n}\n\n/// Adjusts the scale to the closest exactly-representable value.\npub fn closest_representable_scale(scale: f64) -> f64 {\n    // Current fractional-scale Wayland protocol can only represent N / 120 scales.\n    const FRACTIONAL_SCALE_DENOM: f64 = 120.;\n\n    (scale * FRACTIONAL_SCALE_DENOM).round() / FRACTIONAL_SCALE_DENOM\n}\n\n#[cfg(test)]\nmod tests {\n    use insta::assert_snapshot;\n\n    use super::*;\n\n    fn check(size_mm: (i32, i32), resolution: (i32, i32)) -> f64 {\n        guess_monitor_scale(Size::from(size_mm), Size::from(resolution))\n    }\n\n    #[test]\n    fn test_guess_monitor_scale() {\n        // Librem 5; not enough logical area when scaled\n        assert_snapshot!(check((65, 129), (720, 1440)), @\"1.5\");\n        // OnePlus 6\n        assert_snapshot!(check((68, 144), (1080, 2280)), @\"2.5\");\n        // Google Pixel 6a\n        assert_snapshot!(check((64, 142), (1080, 2400)), @\"2.5\");\n        // 13\" MacBook Retina\n        assert_snapshot!(check((286, 179), (2560, 1600)), @\"1.75\");\n        // Surface Laptop Studio\n        assert_snapshot!(check((303, 202), (2400, 1600)), @\"1.5\");\n        // Dell XPS 9320\n        assert_snapshot!(check((290, 180), (3840, 2400)), @\"2.5\");\n        // Lenovo ThinkPad X1 Yoga Gen 6\n        assert_snapshot!(check((300, 190), (3840, 2400)), @\"2.5\");\n        // Generic 23\" 1080p\n        assert_snapshot!(check((509, 286), (1920, 1080)), @\"1\");\n        // Generic 23\" 4K\n        assert_snapshot!(check((509, 286), (3840, 2160)), @\"1.75\");\n        // Generic 27\" 4K\n        assert_snapshot!(check((598, 336), (3840, 2160)), @\"1.5\");\n        // Generic 32\" 4K\n        assert_snapshot!(check((708, 398), (3840, 2160)), @\"1.25\");\n        // Generic 25\" 4K; ideal scale is 1.60, should round to 1.5 and 1.0\n        assert_snapshot!(check((554, 312), (3840, 2160)), @\"1.5\");\n        // Generic 23.5\" 4K; ideal scale is 1.70, should round to 1.75 and 2.0\n        assert_snapshot!(check((522, 294), (3840, 2160)), @\"1.75\");\n        // Lenovo Legion 7 Gen 7 AMD 16\"\n        assert_snapshot!(check((340, 210), (2560, 1600)), @\"1.5\");\n        // Acer Nitro XV320QU LV 31.5\"\n        assert_snapshot!(check((700, 390), (2560, 1440)), @\"1\");\n        // Surface Pro 6\n        assert_snapshot!(check((260, 170), (2736, 1824)), @\"2\");\n    }\n\n    #[test]\n    fn guess_monitor_scale_unknown_size() {\n        assert_eq!(check((0, 0), (1920, 1080)), 1.);\n    }\n\n    #[test]\n    fn test_round_scale() {\n        assert_snapshot!(closest_representable_scale(1.3), @\"1.3\");\n        assert_snapshot!(closest_representable_scale(1.31), @\"1.3083333333333333\");\n        assert_snapshot!(closest_representable_scale(1.32), @\"1.3166666666666667\");\n        assert_snapshot!(closest_representable_scale(1.33), @\"1.3333333333333333\");\n        assert_snapshot!(closest_representable_scale(1.34), @\"1.3416666666666666\");\n        assert_snapshot!(closest_representable_scale(1.35), @\"1.35\");\n    }\n}\n"
  },
  {
    "path": "src/utils/signals.rs",
    "content": "//! We set a signal handler with `calloop::signals::Signals::new`.\n//! This does two things:\n//! 1. It blocks the thread from receiving these signals normally (pthread_sigmask)\n//! 2. It creates a signalfd to read them in the event loop.\n//!\n//! When spawning children, calloop already deals with the signalfd.\n//! `Signals::new` creates it with CLOEXEC, so it will not be inherited by children.\n//!\n//! But, the sigmask is always inherited, so we want to clear it before spawning children.\n//! That way, we don't affect their normal signal handling.\n//!\n//! In particular, if a child doesn't care about signals, we must not block it from receiving them.\n//!\n//! This module provides functions to clear the sigmask. Call them before spawning children.\n//!\n//! Technically, a \"more correct\" solution would be to remember the original sigmask and restore it\n//! after the child exits, but that's painful *and* likely to cause issues, because the user almost\n//! never intended to spawn niri with a nonempty sigmask. It indicates a bug in whoever spawned us,\n//! so we may as well clean up after them (which is easier than not doing so).\n\npub use platform::*;\n\n#[cfg(not(target_os = \"linux\"))]\nmod platform {\n    use std::io;\n\n    // FIXME: implement for FreeBSD. But probably, that should be done in calloop::signals.\n    pub fn listen(_handle: &calloop::LoopHandle<crate::niri::State>) {}\n\n    // These two actually build as-is on FreeBSD, but without our own signal handling in listen(),\n    // they do more harm than good (they block termination signals without actually installing a\n    // termination handler).\n    pub fn block_early() -> io::Result<()> {\n        Ok(())\n    }\n    pub fn unblock_all() -> io::Result<()> {\n        Ok(())\n    }\n}\n\n#[cfg(target_os = \"linux\")]\nmod platform {\n    use std::{io, mem};\n\n    pub fn listen(handle: &calloop::LoopHandle<crate::niri::State>) {\n        use calloop::signals::{Signal, Signals};\n\n        handle\n            .insert_source(\n                Signals::new(&[Signal::SIGINT, Signal::SIGTERM, Signal::SIGHUP]).unwrap(),\n                |event, _, state| {\n                    info!(\"quitting due to receiving signal {:?}\", event.signal());\n                    state.niri.stop_signal.stop();\n                },\n            )\n            .unwrap();\n    }\n\n    // We block the signals early, so that they apply to all threads.\n    // They are then blocked *again* by the `Signals` source. That's fine.\n    pub fn block_early() -> io::Result<()> {\n        set_sigmask(&preferred_sigset()?)\n    }\n\n    pub fn unblock_all() -> io::Result<()> {\n        set_sigmask(&empty_sigset()?)\n    }\n\n    fn empty_sigset() -> io::Result<libc::sigset_t> {\n        let mut sigset = mem::MaybeUninit::uninit();\n        if unsafe { libc::sigemptyset(sigset.as_mut_ptr()) } == 0 {\n            Ok(unsafe { sigset.assume_init() })\n        } else {\n            Err(io::Error::last_os_error())\n        }\n    }\n\n    fn preferred_sigset() -> io::Result<libc::sigset_t> {\n        let mut set = empty_sigset()?;\n        unsafe {\n            add_signal(&mut set, libc::SIGINT)?;\n            add_signal(&mut set, libc::SIGTERM)?;\n            add_signal(&mut set, libc::SIGHUP)?;\n        }\n        Ok(set)\n    }\n\n    // SAFETY: `signum` must be a valid signal number.\n    unsafe fn add_signal(set: &mut libc::sigset_t, signum: libc::c_int) -> io::Result<()> {\n        if unsafe { libc::sigaddset(set, signum) } == 0 {\n            Ok(())\n        } else {\n            Err(io::Error::last_os_error())\n        }\n    }\n\n    fn set_sigmask(set: &libc::sigset_t) -> io::Result<()> {\n        let oldset = std::ptr::null_mut(); // ignore old mask\n        if unsafe { libc::pthread_sigmask(libc::SIG_SETMASK, set, oldset) } == 0 {\n            Ok(())\n        } else {\n            Err(io::Error::last_os_error())\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils/spawning.rs",
    "content": "use std::ffi::OsStr;\nuse std::os::unix::process::CommandExt;\nuse std::path::Path;\nuse std::process::{Child, Command, Stdio};\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::RwLock;\nuse std::{io, thread};\n\nuse atomic::Atomic;\nuse libc::{getrlimit, rlim_t, rlimit, setrlimit, RLIMIT_NOFILE};\nuse niri_config::Environment;\nuse smithay::wayland::xdg_activation::XdgActivationToken;\n\nuse crate::utils::expand_home;\n\npub static REMOVE_ENV_RUST_BACKTRACE: AtomicBool = AtomicBool::new(false);\npub static REMOVE_ENV_RUST_LIB_BACKTRACE: AtomicBool = AtomicBool::new(false);\npub static CHILD_ENV: RwLock<Environment> = RwLock::new(Environment(Vec::new()));\npub static CHILD_DISPLAY: RwLock<Option<String>> = RwLock::new(None);\n\nstatic ORIGINAL_NOFILE_RLIMIT_CUR: Atomic<rlim_t> = Atomic::new(0);\nstatic ORIGINAL_NOFILE_RLIMIT_MAX: Atomic<rlim_t> = Atomic::new(0);\n\n/// Increases the nofile rlimit to the maximum and stores the original value.\npub fn store_and_increase_nofile_rlimit() {\n    let mut rlim = rlimit {\n        rlim_cur: 0,\n        rlim_max: 0,\n    };\n    if unsafe { getrlimit(RLIMIT_NOFILE, &mut rlim) } != 0 {\n        let err = io::Error::last_os_error();\n        warn!(\"error getting nofile rlimit: {err:?}\");\n        return;\n    }\n\n    ORIGINAL_NOFILE_RLIMIT_CUR.store(rlim.rlim_cur, Ordering::SeqCst);\n    ORIGINAL_NOFILE_RLIMIT_MAX.store(rlim.rlim_max, Ordering::SeqCst);\n\n    trace!(\n        \"changing nofile rlimit from {} to {}\",\n        rlim.rlim_cur,\n        rlim.rlim_max\n    );\n    rlim.rlim_cur = rlim.rlim_max;\n\n    if unsafe { setrlimit(RLIMIT_NOFILE, &rlim) } != 0 {\n        let err = io::Error::last_os_error();\n        warn!(\"error setting nofile rlimit: {err:?}\");\n    }\n}\n\n/// Restores the original nofile rlimit.\npub fn restore_nofile_rlimit() {\n    let rlim_cur = ORIGINAL_NOFILE_RLIMIT_CUR.load(Ordering::SeqCst);\n    let rlim_max = ORIGINAL_NOFILE_RLIMIT_MAX.load(Ordering::SeqCst);\n\n    if rlim_cur == 0 {\n        return;\n    }\n\n    let rlim = rlimit { rlim_cur, rlim_max };\n    unsafe { setrlimit(RLIMIT_NOFILE, &rlim) };\n}\n\n/// Spawns the command to run independently of the compositor.\npub fn spawn<T: AsRef<OsStr> + Send + 'static>(command: Vec<T>, token: Option<XdgActivationToken>) {\n    let _span = tracy_client::span!();\n\n    if command.is_empty() {\n        return;\n    }\n\n    // Spawning and waiting takes some milliseconds, so do it in a thread.\n    let res = thread::Builder::new()\n        .name(\"Command Spawner\".to_owned())\n        .spawn(move || {\n            let (command, args) = command.split_first().unwrap();\n            spawn_sync(command, args, token);\n        });\n\n    if let Err(err) = res {\n        warn!(\"error spawning a thread to spawn the command: {err:?}\");\n    }\n}\n\n/// Spawns the command through the shell.\n///\n/// We hardcode `sh -c`, consistent with other compositors:\n///\n/// - https://github.com/swaywm/sway/blob/b3dcde8d69c3f1304b076968a7a64f54d0c958be/sway/commands/exec_always.c#L64\n/// - https://github.com/hyprwm/Hyprland/blob/1ac1ff457ab8ef1ae6a8f2ab17ee7965adfa729f/src/managers/KeybindManager.cpp#L987\npub fn spawn_sh(command: String, token: Option<XdgActivationToken>) {\n    spawn(vec![String::from(\"sh\"), String::from(\"-c\"), command], token);\n}\n\nfn spawn_sync(\n    command: impl AsRef<OsStr>,\n    args: impl IntoIterator<Item = impl AsRef<OsStr>>,\n    token: Option<XdgActivationToken>,\n) {\n    let _span = tracy_client::span!();\n\n    let mut command = command.as_ref();\n\n    // Expand `~` at the start.\n    let expanded = expand_home(Path::new(command));\n    match &expanded {\n        Ok(Some(expanded)) => command = expanded.as_ref(),\n        Ok(None) => (),\n        Err(err) => {\n            warn!(\"error expanding ~: {err:?}\");\n        }\n    }\n\n    let mut process = Command::new(command);\n    process\n        .args(args)\n        .stdin(Stdio::null())\n        .stdout(Stdio::null())\n        .stderr(Stdio::null());\n\n    // Remove RUST_BACKTRACE and RUST_LIB_BACKTRACE from the environment if needed.\n    if REMOVE_ENV_RUST_BACKTRACE.load(Ordering::Relaxed) {\n        process.env_remove(\"RUST_BACKTRACE\");\n    }\n    if REMOVE_ENV_RUST_LIB_BACKTRACE.load(Ordering::Relaxed) {\n        process.env_remove(\"RUST_LIB_BACKTRACE\");\n    }\n\n    // Set DISPLAY if needed.\n    let display = CHILD_DISPLAY.read().unwrap();\n    if let Some(display) = &*display {\n        process.env(\"DISPLAY\", display);\n    } else {\n        process.env_remove(\"DISPLAY\");\n    }\n\n    // Set configured environment.\n    let env = CHILD_ENV.read().unwrap();\n    for var in &env.0 {\n        if let Some(value) = &var.value {\n            process.env(&var.name, value);\n        } else {\n            process.env_remove(&var.name);\n        }\n    }\n    drop(env);\n\n    if let Some(token) = token.as_ref() {\n        process.env(\"XDG_ACTIVATION_TOKEN\", token.as_str());\n        process.env(\"DESKTOP_STARTUP_ID\", token.as_str());\n    }\n\n    unsafe { process.pre_exec(crate::utils::signals::unblock_all) };\n\n    let Some(mut child) = do_spawn(command, process) else {\n        return;\n    };\n\n    match child.wait() {\n        Ok(status) => {\n            if !status.success() {\n                warn!(\"child did not exit successfully: {status:?}\");\n            }\n        }\n        Err(err) => {\n            warn!(\"error waiting for child: {err:?}\");\n        }\n    }\n}\n\n#[cfg(not(feature = \"systemd\"))]\nfn do_spawn(command: &OsStr, mut process: Command) -> Option<Child> {\n    unsafe {\n        // Double-fork to avoid having to waitpid the child.\n        process.pre_exec(move || {\n            match libc::fork() {\n                -1 => return Err(io::Error::last_os_error()),\n                0 => (),\n                _ => libc::_exit(0),\n            }\n\n            restore_nofile_rlimit();\n\n            Ok(())\n        });\n    }\n\n    let child = match process.spawn() {\n        Ok(child) => child,\n        Err(err) => {\n            warn!(\"error spawning {command:?}: {err:?}\");\n            return None;\n        }\n    };\n\n    Some(child)\n}\n\n#[cfg(feature = \"systemd\")]\nuse systemd::do_spawn;\n\n#[cfg(feature = \"systemd\")]\nmod systemd {\n    use std::os::fd::{AsFd, AsRawFd, FromRawFd, OwnedFd};\n\n    use smithay::reexports::rustix;\n    use smithay::reexports::rustix::io::{close, read, retry_on_intr, write};\n    use smithay::reexports::rustix::pipe::{pipe_with, PipeFlags};\n\n    use super::*;\n\n    pub fn do_spawn(command: &OsStr, mut process: Command) -> Option<Child> {\n        #[cfg(target_env = \"gnu\")]\n        use libc::close_range;\n        #[cfg(target_os = \"openbsd\")]\n        use libc::closefrom;\n\n        #[cfg(not(target_env = \"gnu\"))] // musl\n        pub fn close_range(first: libc::c_uint, last: libc::c_uint, flags: libc::c_uint) -> i64 {\n            unsafe {\n                libc::syscall(\n                    libc::SYS_close_range,\n                    first as usize,\n                    last as usize,\n                    flags as usize,\n                )\n            }\n        }\n\n        // When running as a systemd session, we want to put children into their own transient\n        // scopes in order to separate them from the niri process. This is helpful for\n        // example to prevent the OOM killer from taking down niri together with a\n        // misbehaving client.\n        //\n        // Putting a child into a scope is done by calling systemd's StartTransientUnit D-Bus method\n        // with a PID. Unfortunately, there seems to be a race in systemd where if the child exits\n        // at just the right time, the transient unit will be created but empty, so it will\n        // linger around forever.\n        //\n        // To prevent this, we'll use our double-fork (done for a separate reason) to help. In our\n        // intermediate child we will send back the grandchild PID, and in niri we will create a\n        // transient scope with both our intermediate child and the grandchild PIDs set. Only then\n        // we will signal our intermediate child to exit. This way, even if the grandchild\n        // exits quickly, a non-empty scope will be created (with just our intermediate\n        // child), then cleaned up when our intermediate child exits.\n\n        // Make a pipe to receive the grandchild PID.\n\n        let (pipe_pid_read, pipe_pid_write) = pipe_with(PipeFlags::CLOEXEC)\n            .map_err(|err| {\n                warn!(\"error creating a pipe to transfer child PID: {err:?}\");\n            })\n            .ok()\n            .unzip();\n        // Make a pipe to wait in the intermediate child.\n        let (pipe_wait_read, pipe_wait_write) = pipe_with(PipeFlags::CLOEXEC)\n            .map_err(|err| {\n                warn!(\"error creating a pipe for child to wait on: {err:?}\");\n            })\n            .ok()\n            .unzip();\n\n        unsafe {\n            // The fds will be duplicated after a fork and closed on exec or exit automatically. Get\n            // the raw fd inside so that it's not closed any extra times.\n            let mut pipe_pid_read_fd = pipe_pid_read.as_ref().map(|fd| fd.as_raw_fd());\n            let mut pipe_pid_write_fd = pipe_pid_write.as_ref().map(|fd| fd.as_raw_fd());\n            let mut pipe_wait_read_fd = pipe_wait_read.as_ref().map(|fd| fd.as_raw_fd());\n            let mut pipe_wait_write_fd = pipe_wait_write.as_ref().map(|fd| fd.as_raw_fd());\n\n            // Double-fork to avoid having to waitpid the child.\n            process.pre_exec(move || {\n                // Close FDs that we don't need. Especially important for the write ones to unblock\n                // the readers.\n                if let Some(fd) = pipe_pid_read_fd.take() {\n                    close(fd);\n                }\n                if let Some(fd) = pipe_wait_write_fd.take() {\n                    close(fd);\n                }\n\n                // Convert the FDs to OwnedFd, which will close them in all of our fork paths.\n                let pipe_pid_write = pipe_pid_write_fd.take().map(|fd| OwnedFd::from_raw_fd(fd));\n                let pipe_wait_read = pipe_wait_read_fd.take().map(|fd| OwnedFd::from_raw_fd(fd));\n\n                match libc::fork() {\n                    -1 => return Err(io::Error::last_os_error()),\n                    0 => (),\n                    grandchild_pid => {\n                        // Send back the PID.\n                        if let Some(pipe) = pipe_pid_write {\n                            let _ = write_all(pipe, &grandchild_pid.to_ne_bytes());\n                        }\n\n                        // Wait until the parent signals us to exit.\n                        if let Some(pipe) = pipe_wait_read {\n                            // We're going to exit afterwards. Close all other FDs to allow\n                            // Command::spawn() to return in the parent process.\n                            #[cfg(not(target_os = \"openbsd\"))]\n                            {\n                                let raw = pipe.as_raw_fd() as u32;\n                                let _ = close_range(0, raw - 1, 0);\n                                let _ = close_range(raw + 1, !0, 0);\n                            }\n                            #[cfg(target_os = \"openbsd\")]\n                            {\n                                let raw = pipe.as_raw_fd();\n                                for fd in 0..raw {\n                                    close(fd);\n                                }\n                                closefrom(raw + 1);\n                            }\n\n                            let _ = read_all(pipe, &mut [0]);\n                        }\n\n                        libc::_exit(0)\n                    }\n                }\n\n                restore_nofile_rlimit();\n\n                Ok(())\n            });\n        }\n\n        let child = match process.spawn() {\n            Ok(child) => child,\n            Err(err) => {\n                warn!(\"error spawning {command:?}: {err:?}\");\n                return None;\n            }\n        };\n\n        drop(pipe_pid_write);\n        drop(pipe_wait_read);\n\n        // Wait for the grandchild PID.\n        if let Some(pipe) = pipe_pid_read {\n            let mut buf = [0; 4];\n            match read_all(pipe, &mut buf) {\n                Ok(()) => {\n                    let pid = i32::from_ne_bytes(buf);\n                    trace!(\"spawned PID: {pid}\");\n\n                    // Start a systemd scope for the grandchild.\n                    if let Err(err) = start_systemd_scope(command, child.id(), pid as u32) {\n                        trace!(\"error starting systemd scope for spawned command: {err:?}\");\n                    }\n                }\n                Err(err) => {\n                    warn!(\"error reading child PID: {err:?}\");\n                }\n            }\n        }\n\n        // Signal the intermediate child to exit now that we're done trying to creating a systemd\n        // scope.\n        trace!(\"signaling child to exit\");\n        drop(pipe_wait_write);\n\n        Some(child)\n    }\n\n    fn write_all(fd: impl AsFd, buf: &[u8]) -> rustix::io::Result<()> {\n        let mut written = 0;\n        loop {\n            let n = retry_on_intr(|| write(&fd, &buf[written..]))?;\n            if n == 0 {\n                return Err(rustix::io::Errno::CANCELED);\n            }\n\n            written += n;\n            if written == buf.len() {\n                return Ok(());\n            }\n        }\n    }\n\n    fn read_all(fd: impl AsFd, buf: &mut [u8]) -> rustix::io::Result<()> {\n        let mut start = 0;\n        loop {\n            let n = retry_on_intr(|| read(&fd, &mut buf[start..]))?;\n            if n == 0 {\n                return Err(rustix::io::Errno::CANCELED);\n            }\n\n            start += n;\n            if start == buf.len() {\n                return Ok(());\n            }\n        }\n    }\n\n    /// Puts a (newly spawned) pid into a transient systemd scope.\n    ///\n    /// This separates the pid from the compositor scope, which for example prevents the OOM killer\n    /// from bringing down the compositor together with a misbehaving client.\n    fn start_systemd_scope(\n        name: &OsStr,\n        intermediate_pid: u32,\n        child_pid: u32,\n    ) -> anyhow::Result<()> {\n        use std::fmt::Write as _;\n        use std::os::unix::ffi::OsStrExt;\n        use std::sync::OnceLock;\n\n        use anyhow::Context;\n        use zbus::zvariant::{OwnedObjectPath, Value};\n\n        use crate::utils::IS_SYSTEMD_SERVICE;\n\n        // We only start transient scopes if we're a systemd service ourselves.\n        if !IS_SYSTEMD_SERVICE.load(Ordering::Relaxed) {\n            return Ok(());\n        }\n\n        let _span = tracy_client::span!();\n\n        // Extract the basename.\n        let name = Path::new(name).file_name().unwrap_or(name);\n\n        let mut scope_name = String::from(\"app-niri-\");\n\n        // Escape for systemd similarly to libgnome-desktop, which says it had adapted this from\n        // systemd source.\n        for &c in name.as_bytes() {\n            if c.is_ascii_alphanumeric() || matches!(c, b':' | b'_' | b'.') {\n                scope_name.push(char::from(c));\n            } else {\n                let _ = write!(scope_name, \"\\\\x{c:02x}\");\n            }\n        }\n\n        let _ = write!(scope_name, \"-{child_pid}.scope\");\n\n        // Ask systemd to start a transient scope.\n        static CONNECTION: OnceLock<zbus::Result<zbus::blocking::Connection>> = OnceLock::new();\n        let conn = CONNECTION\n            .get_or_init(zbus::blocking::Connection::session)\n            .clone()\n            .context(\"error connecting to session bus\")?;\n\n        let proxy = zbus::blocking::Proxy::new(\n            &conn,\n            \"org.freedesktop.systemd1\",\n            \"/org/freedesktop/systemd1\",\n            \"org.freedesktop.systemd1.Manager\",\n        )\n        .context(\"error creating a Proxy\")?;\n\n        let signals = proxy\n            .receive_signal(\"JobRemoved\")\n            .context(\"error creating a signal iterator\")?;\n\n        let pids: &[_] = &[intermediate_pid, child_pid];\n        let properties: &[_] = &[\n            (\"PIDs\", Value::new(pids)),\n            (\"CollectMode\", Value::new(\"inactive-or-failed\")),\n        ];\n        let aux: &[(&str, &[(&str, Value)])] = &[];\n\n        let job: OwnedObjectPath = proxy\n            .call(\"StartTransientUnit\", &(scope_name, \"fail\", properties, aux))\n            .context(\"error calling StartTransientUnit\")?;\n\n        trace!(\"waiting for JobRemoved\");\n        for message in signals {\n            let body = message.body();\n            let body: (u32, OwnedObjectPath, &str, &str) =\n                body.deserialize().context(\"error parsing signal\")?;\n\n            if body.1 == job {\n                // Our transient unit had started, we're good to exit the intermediate child.\n                break;\n            }\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/utils/transaction.rs",
    "content": "use std::cell::RefCell;\nuse std::rc::Rc;\nuse std::sync::atomic::AtomicBool;\nuse std::sync::mpsc::Sender;\nuse std::sync::{Arc, Mutex, Weak};\nuse std::time::{Duration, Instant};\n\nuse atomic::Ordering;\nuse calloop::ping::{make_ping, Ping};\nuse calloop::timer::{TimeoutAction, Timer};\nuse calloop::LoopHandle;\nuse smithay::reexports::wayland_server::Client;\nuse smithay::wayland::compositor::{Blocker, BlockerState};\n\n/// Default time limit, after which the transaction completes.\n///\n/// Serves to avoid hanging when a client fails to respond to a configure promptly.\nconst TIME_LIMIT: Duration = Duration::from_millis(300);\n\n/// Transaction between Wayland clients.\n///\n/// How to use it:\n/// 1. Create a transaction with [`Transaction::new()`].\n/// 2. Clone it as many times as you need.\n/// 3. Before adding the transaction as a commit blocker, remember to call\n///    [`Transaction::add_notification()`] to receive a notification when the transaction completes.\n/// 4. Before adding the transaction as a commit blocker, remember to call\n///    [`Transaction::register_deadline_timer()`] to make sure the transaction completes when\n///    reaching the deadline.\n/// 5. In your surface pre-commit handler, if the transaction corresponding to that commit isn't\n///    ready, get a blocker with [`Transaction::blocker()`] and add it to the surface.\n#[derive(Debug, Clone)]\npub struct Transaction {\n    inner: Arc<Inner>,\n    deadline: Rc<RefCell<Deadline>>,\n}\n\n/// Blocker for a [`Transaction`].\n#[derive(Debug)]\npub struct TransactionBlocker(Weak<Inner>);\n\n#[derive(Debug)]\nenum Deadline {\n    NotRegistered(Instant),\n    Registered { remove: Ping },\n}\n\n#[derive(Debug)]\nstruct Inner {\n    /// Whether the transaction is completed.\n    completed: AtomicBool,\n    /// Notifications to send out upon completing the transaction.\n    notifications: Mutex<Option<(Sender<Client>, Vec<Client>)>>,\n}\n\nimpl Transaction {\n    /// Creates a new transaction.\n    #[allow(clippy::new_without_default)]\n    pub fn new() -> Self {\n        Self {\n            inner: Arc::new(Inner::new()),\n            deadline: Rc::new(RefCell::new(Deadline::NotRegistered(\n                Instant::now() + TIME_LIMIT,\n            ))),\n        }\n    }\n\n    /// Gets a blocker for this transaction.\n    pub fn blocker(&self) -> TransactionBlocker {\n        trace!(transaction = ?Arc::as_ptr(&self.inner), \"generating blocker\");\n        TransactionBlocker(Arc::downgrade(&self.inner))\n    }\n\n    /// Adds a notification for when this transaction completes.\n    pub fn add_notification(&self, sender: Sender<Client>, client: Client) {\n        if self.is_completed() {\n            error!(\"tried to add notification to a completed transaction\");\n            return;\n        }\n\n        let mut guard = self.inner.notifications.lock().unwrap();\n        guard.get_or_insert((sender, Vec::new())).1.push(client);\n    }\n\n    /// Registers this transaction's deadline timer on an event loop.\n    pub fn register_deadline_timer<T: 'static>(&self, event_loop: &LoopHandle<'static, T>) {\n        let mut cell = self.deadline.borrow_mut();\n        if let Deadline::NotRegistered(deadline) = *cell {\n            let timer = Timer::from_deadline(deadline);\n            let inner = Arc::downgrade(&self.inner);\n            let token = event_loop\n                .insert_source(timer, move |_, _, _| {\n                    let _span = trace_span!(\"deadline timer\", transaction = ?Weak::as_ptr(&inner))\n                        .entered();\n\n                    // FIXME: come up with some way to control the deadline timer from tests.\n                    #[cfg(not(test))]\n                    if let Some(inner) = inner.upgrade() {\n                        trace!(\"deadline reached, completing transaction\");\n                        inner.complete();\n                    } else {\n                        // We should remove the timer automatically. But this callback can still\n                        // just happen to run while the ping callback is scheduled, leading to this\n                        // branch being legitimately taken.\n                        trace!(\"transaction completed without removing the timer\");\n                    }\n\n                    TimeoutAction::Drop\n                })\n                .unwrap();\n\n            // Add a ping source that will be used to remove the timer automatically.\n            let (ping, source) = make_ping().unwrap();\n            let loop_handle = event_loop.clone();\n            event_loop\n                .insert_source(source, move |_, _, _| {\n                    loop_handle.remove(token);\n                })\n                .unwrap();\n\n            *cell = Deadline::Registered { remove: ping };\n        }\n    }\n\n    /// Returns whether this transaction has already completed.\n    pub fn is_completed(&self) -> bool {\n        self.inner.is_completed()\n    }\n\n    /// Returns whether this is the last instance of this transaction.\n    pub fn is_last(&self) -> bool {\n        Arc::strong_count(&self.inner) == 1\n    }\n}\n\nimpl Drop for Transaction {\n    fn drop(&mut self) {\n        let _span = trace_span!(\"drop\", transaction = ?Arc::as_ptr(&self.inner)).entered();\n\n        if self.is_last() {\n            // If this was the last transaction, complete it.\n            trace!(\"last transaction dropped, completing\");\n            self.inner.complete();\n\n            // Also remove the timer.\n            if let Deadline::Registered { remove } = &*self.deadline.borrow() {\n                remove.ping();\n            };\n        }\n    }\n}\n\nimpl TransactionBlocker {\n    pub fn completed() -> Self {\n        Self(Weak::new())\n    }\n}\n\nimpl Blocker for TransactionBlocker {\n    fn state(&self) -> BlockerState {\n        if self.0.upgrade().is_none_or(|x| x.is_completed()) {\n            BlockerState::Released\n        } else {\n            BlockerState::Pending\n        }\n    }\n}\n\nimpl Inner {\n    fn new() -> Self {\n        Self {\n            completed: AtomicBool::new(false),\n            notifications: Mutex::new(None),\n        }\n    }\n\n    fn is_completed(&self) -> bool {\n        self.completed.load(Ordering::Relaxed)\n    }\n\n    fn complete(&self) {\n        self.completed.store(true, Ordering::Relaxed);\n\n        let mut guard = self.notifications.lock().unwrap();\n        if let Some((sender, clients)) = guard.take() {\n            for client in clients {\n                if let Err(err) = sender.send(client) {\n                    warn!(\"error sending blocker notification: {err:?}\");\n                };\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils/vblank_throttle.rs",
    "content": "//! VBlank throttling.\n//!\n//! Some buggy drivers deliver VBlanks way earlier than necessary. This helper throttles the VBlank\n//! in such cases to avoid tearing and to get more consistent timings.\n\nuse std::time::Duration;\n\nuse calloop::timer::{TimeoutAction, Timer};\nuse calloop::{LoopHandle, RegistrationToken};\n\nuse crate::niri::State;\n\n#[derive(Debug)]\npub struct VBlankThrottle {\n    event_loop: LoopHandle<'static, State>,\n    last_vblank_timestamp: Option<Duration>,\n    throttle_timer_token: Option<RegistrationToken>,\n    printed_warning: bool,\n    output_name: String,\n}\n\nimpl VBlankThrottle {\n    pub fn new(event_loop: LoopHandle<'static, State>, output_name: String) -> Self {\n        Self {\n            event_loop,\n            last_vblank_timestamp: None,\n            throttle_timer_token: None,\n            printed_warning: false,\n            output_name,\n        }\n    }\n\n    pub fn throttle(\n        &mut self,\n        refresh_interval: Option<Duration>,\n        timestamp: Duration,\n        mut call_vblank: impl FnMut(&mut State) + 'static,\n    ) -> bool {\n        if let Some(token) = self.throttle_timer_token.take() {\n            self.event_loop.remove(token);\n        }\n\n        if let Some((last, refresh)) = Option::zip(self.last_vblank_timestamp, refresh_interval) {\n            let passed = timestamp.saturating_sub(last);\n            if passed < refresh / 2 {\n                if !self.printed_warning {\n                    self.printed_warning = true;\n                    warn!(\n                        \"output {} running faster than expected, throttling vblanks: \\\n                         expected refresh {refresh:?}, got vblank after {passed:?}\",\n                        self.output_name\n                    );\n                }\n\n                let remaining = refresh - passed;\n                let token = self\n                    .event_loop\n                    .insert_source(Timer::from_duration(remaining), move |_, _, state| {\n                        call_vblank(state);\n                        TimeoutAction::Drop\n                    })\n                    .unwrap();\n                self.throttle_timer_token = Some(token);\n                return true;\n            }\n        }\n\n        self.last_vblank_timestamp = Some(timestamp);\n        false\n    }\n}\n"
  },
  {
    "path": "src/utils/watcher.rs",
    "content": "//! File modification watcher.\n\nuse std::collections::HashMap;\nuse std::path::{Path, PathBuf};\nuse std::sync::mpsc;\nuse std::time::{Duration, SystemTime};\nuse std::{io, thread};\n\nuse niri_config::{Config, ConfigParseResult, ConfigPath};\nuse smithay::reexports::calloop::channel::SyncSender;\n\nuse crate::niri::State;\n\nconst POLLING_INTERVAL: Duration = Duration::from_millis(500);\n\npub struct Watcher {\n    load_config: mpsc::Sender<Option<String>>,\n}\n\nstruct WatcherInner {\n    /// The paths we're watching.\n    path: ConfigPath,\n\n    /// Last observed props of the watched file.\n    last_props: Option<Props>,\n\n    /// Last observed props for included files.\n    includes: HashMap<PathBuf, Option<Props>>,\n}\n\n/// Properties of the watched file.\n///\n/// Equality on this means the file did not change.\n#[derive(Debug, PartialEq, Eq)]\nstruct Props {\n    /// Modification time of the watched file.\n    mtime: SystemTime,\n\n    /// Canonical form of the watched path.\n    ///\n    /// We store the absolute path in addition to mtime to account for symlinked configs where the\n    /// symlink target may change without mtime. This is common on nix where everything is a\n    /// symlink to /nix/store, which keeps no mtime (= 1970-01-01).\n    canonical: PathBuf,\n}\n\n#[derive(Debug, PartialEq, Eq)]\nenum CheckResult {\n    Missing,\n    Unchanged,\n    Changed,\n}\n\nimpl Watcher {\n    pub fn new(\n        path: ConfigPath,\n        includes: Vec<PathBuf>,\n        mut process: impl FnMut(&ConfigPath) -> ConfigParseResult<Config, ()> + Send + 'static,\n        changed: SyncSender<Result<Config, ()>>,\n    ) -> Self {\n        let (load_config, load_config_rx) = mpsc::channel();\n\n        thread::Builder::new()\n            .name(format!(\"Filesystem Watcher for {path:?}\"))\n            .spawn(move || {\n                let mut inner = WatcherInner::new(path, includes);\n\n                loop {\n                    let mut should_load = match load_config_rx.recv_timeout(POLLING_INTERVAL) {\n                        Ok(path) => {\n                            if let Some(path) = path {\n                                inner = WatcherInner::new(\n                                    ConfigPath::Explicit(PathBuf::from(path)),\n                                    Vec::new(),\n                                );\n                            }\n                            true\n                        }\n                        Err(mpsc::RecvTimeoutError::Disconnected) => break,\n                        Err(mpsc::RecvTimeoutError::Timeout) => false,\n                    };\n\n                    match inner.check() {\n                        CheckResult::Missing => continue,\n                        CheckResult::Unchanged => (),\n                        CheckResult::Changed => {\n                            trace!(\"config file changed\");\n                            should_load = true;\n                        }\n                    }\n\n                    if should_load {\n                        let res = process(&inner.path);\n\n                        if let Err(err) = changed.send(res.config) {\n                            warn!(\"error sending change notification: {err:?}\");\n                            break;\n                        }\n\n                        // There's a bit of time here between reading the config and reading\n                        // properties of included files where an included file could change and\n                        // remain unnoticed by the watcher. Not sure there's any good way around it\n                        // though since we don't know the final set of includes until the config is\n                        // parsed.\n                        inner.set_includes(res.includes);\n                    }\n                }\n\n                debug!(\"exiting watcher thread for {:?}\", inner.path);\n            })\n            .unwrap();\n\n        Self { load_config }\n    }\n\n    pub fn load_config(&self, path: Option<String>) {\n        let _ = self.load_config.send(path);\n    }\n}\n\nimpl Props {\n    fn from_path(path: &Path) -> io::Result<Self> {\n        let canonical = path.canonicalize()?;\n        let mtime = canonical.metadata()?.modified()?;\n        Ok(Self { mtime, canonical })\n    }\n\n    fn from_config_path(config_path: &ConfigPath) -> io::Result<Self> {\n        match config_path {\n            ConfigPath::Explicit(path) => Self::from_path(path),\n            ConfigPath::Regular {\n                user_path,\n                system_path,\n            } => Self::from_path(user_path).or_else(|_| Self::from_path(system_path)),\n        }\n    }\n}\n\nimpl WatcherInner {\n    pub fn new(path: ConfigPath, includes: Vec<PathBuf>) -> Self {\n        let last_props = Props::from_config_path(&path).ok();\n\n        let mut rv = Self {\n            path,\n            last_props,\n            includes: HashMap::new(),\n        };\n        rv.set_includes(includes);\n        rv\n    }\n\n    pub fn check(&mut self) -> CheckResult {\n        if let Ok(new_props) = Props::from_config_path(&self.path) {\n            if self.last_props.as_ref() != Some(&new_props) {\n                self.last_props = Some(new_props);\n                CheckResult::Changed\n            } else {\n                for (path, last_props) in &mut self.includes {\n                    let new_props = Props::from_path(path).ok();\n\n                    // If an include goes missing while the main config file is unchanged, we\n                    // consider that a change and reload.\n                    if *last_props != new_props {\n                        return CheckResult::Changed;\n                    }\n                }\n\n                CheckResult::Unchanged\n            }\n        } else {\n            CheckResult::Missing\n        }\n    }\n\n    fn set_includes(&mut self, includes: Vec<PathBuf>) {\n        self.includes = includes\n            .into_iter()\n            .map(|path| {\n                let props = Props::from_path(&path).ok();\n                (path, props)\n            })\n            .collect();\n    }\n}\n\npub fn setup(state: &mut State, config_path: &ConfigPath, includes: Vec<PathBuf>) {\n    // Parsing the config actually takes > 20 ms on my beefy machine, so let's do it on the\n    // watcher thread.\n    let process = |path: &ConfigPath| {\n        path.load().map_config_res(|res| {\n            res.map_err(|err| {\n                warn!(\"{err:?}\");\n            })\n        })\n    };\n\n    let (tx, rx) = calloop::channel::sync_channel(1);\n    state\n        .niri\n        .event_loop\n        .insert_source(\n            rx,\n            |event: calloop::channel::Event<Result<Config, ()>>, _, state| match event {\n                calloop::channel::Event::Msg(config) => {\n                    let failed = config.is_err();\n                    state.reload_config(config);\n                    state.ipc_config_loaded(failed);\n                }\n                calloop::channel::Event::Closed => (),\n            },\n        )\n        .unwrap();\n\n    let watcher = Watcher::new(config_path.clone(), includes, process, tx);\n    state.niri.config_file_watcher = Some(watcher);\n}\n\n#[cfg(test)]\nmod tests {\n    use std::error::Error;\n    use std::fs::{self, File, FileTimes};\n    use std::io::Write;\n\n    use xshell::{cmd, Shell, TempDir};\n\n    use super::*;\n\n    type Result<T = (), E = Box<dyn Error>> = std::result::Result<T, E>;\n\n    fn canon(config_path: &ConfigPath) -> &PathBuf {\n        match config_path {\n            ConfigPath::Explicit(path) => path,\n            ConfigPath::Regular {\n                user_path,\n                system_path,\n            } => {\n                if user_path.exists() {\n                    user_path\n                } else {\n                    system_path\n                }\n            }\n        }\n    }\n\n    enum TestPath<P> {\n        Explicit(P),\n        Regular { user_path: P, system_path: P },\n    }\n\n    impl<P: AsRef<Path>> TestPath<P> {\n        fn setup<Discard>(\n            self,\n            setup: impl FnOnce(&Shell) -> xshell::Result<Discard>,\n        ) -> TestSetup {\n            self.setup_any(|sh| {\n                _ = setup(sh)?;\n                Ok(())\n            })\n        }\n\n        fn without_setup(self) -> TestSetup {\n            self.setup_any(|_| Ok(())).assert_initial_not_exists()\n        }\n\n        fn setup_any(self, setup: impl FnOnce(&Shell) -> Result) -> TestSetup {\n            let sh = Shell::new().unwrap();\n            let temp_dir = sh.create_temp_dir().unwrap();\n            sh.change_dir(temp_dir.path());\n\n            let dir = sh.current_dir();\n            let config_path = match self {\n                TestPath::Explicit(path) => ConfigPath::Explicit(dir.join(path)),\n                TestPath::Regular {\n                    user_path,\n                    system_path,\n                } => ConfigPath::Regular {\n                    user_path: dir.join(user_path),\n                    system_path: dir.join(system_path),\n                },\n            };\n\n            setup(&sh).unwrap();\n\n            TestSetup {\n                sh,\n                config_path,\n                _temp_dir: temp_dir,\n            }\n        }\n    }\n\n    struct TestSetup {\n        sh: Shell,\n        config_path: ConfigPath,\n        _temp_dir: TempDir,\n    }\n\n    impl TestSetup {\n        fn assert_initial_not_exists(self) -> Self {\n            let canon = canon(&self.config_path);\n            assert!(!canon.exists(), \"initial should not exist\");\n            self\n        }\n\n        fn assert_initial(self, expected: &str) -> Self {\n            let canon = canon(&self.config_path);\n            assert!(canon.exists(), \"initial should exist at {canon:?}\");\n            let actual = fs::read_to_string(canon).unwrap();\n            assert_eq!(actual, expected, \"initial file contents do not match\");\n            self\n        }\n\n        fn run(self, body: impl FnOnce(&Shell, &mut TestUtil) -> Result) -> Result {\n            let TestSetup {\n                sh, config_path, ..\n            } = self;\n\n            let includes = config_path.load().includes;\n            let mut test = TestUtil {\n                watcher: WatcherInner::new(config_path, includes),\n            };\n\n            // don't trigger before we start\n            test.assert_unchanged();\n            // pass_time() inside assert_unchanged() ensures that mtime\n            // isn't the same as the initial time\n\n            body(&sh, &mut test)?;\n\n            // nothing should trigger after the test runs\n            test.assert_unchanged();\n\n            Ok(())\n        }\n    }\n\n    struct TestUtil {\n        watcher: WatcherInner,\n    }\n\n    impl TestUtil {\n        // Ensures that mtime is different between writes in the tests.\n        fn pass_time(&self) {\n            thread::sleep(Duration::from_millis(50));\n        }\n\n        fn assert_unchanged(&mut self) {\n            let res = self.watcher.check();\n\n            // This may be Missing or Unchanged, both are fine.\n            assert_ne!(\n                res,\n                CheckResult::Changed,\n                \"watcher should not have noticed any changes\"\n            );\n\n            self.pass_time();\n        }\n\n        fn assert_changed_to(&mut self, expected: &str) {\n            let res = self.watcher.check();\n            assert_eq!(\n                res,\n                CheckResult::Changed,\n                \"watcher should have noticed a change, but it didn't\"\n            );\n\n            let new_path = canon(&self.watcher.path);\n            let actual = fs::read_to_string(new_path).unwrap();\n            assert_eq!(actual, expected, \"wrong file contents\");\n\n            self.watcher.set_includes(Config::load(new_path).includes);\n\n            self.pass_time();\n        }\n    }\n\n    #[test]\n    fn change_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.write_file(\"niri/config.kdl\", \"a\"))\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.write_file(\"niri/config.kdl\", \"b\")?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn overwrite_but_dont_change_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.write_file(\"niri/config.kdl\", \"a\"))\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.write_file(\"niri/config.kdl\", \"a\")?;\n                test.assert_changed_to(\"a\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn touch_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.write_file(\"niri/config.kdl\", \"a\"))\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                cmd!(sh, \"touch niri/config.kdl\").run()?;\n                test.assert_changed_to(\"a\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn create_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.create_dir(\"niri\"))\n            .assert_initial_not_exists()\n            .run(|sh, test| {\n                sh.write_file(\"niri/config.kdl\", \"a\")?;\n                test.assert_changed_to(\"a\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn create_dir_and_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .without_setup()\n            .run(|sh, test| {\n                sh.write_file(\"niri/config.kdl\", \"a\")?;\n                test.assert_changed_to(\"a\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn change_linked_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri/config2.kdl\", \"a\")?;\n                cmd!(sh, \"ln -sf config2.kdl niri/config.kdl\").run()\n            })\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.write_file(\"niri/config2.kdl\", \"b\")?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn change_file_in_linked_dir() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri2/config.kdl\", \"a\")?;\n                cmd!(sh, \"ln -s niri2 niri\").run()\n            })\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.write_file(\"niri2/config.kdl\", \"b\")?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn remove_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.write_file(\"niri/config.kdl\", \"a\"))\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.remove_path(\"niri/config.kdl\")?;\n                test.assert_unchanged();\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn remove_dir() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.write_file(\"niri/config.kdl\", \"a\"))\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.remove_path(\"niri\")?;\n                test.assert_unchanged();\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn recreate_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.write_file(\"niri/config.kdl\", \"a\"))\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.remove_path(\"niri/config.kdl\")?;\n                sh.write_file(\"niri/config.kdl\", \"b\")?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn recreate_dir() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri/config.kdl\", \"a\")?;\n                Ok(())\n            })\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.remove_path(\"niri\")?;\n                sh.write_file(\"niri/config.kdl\", \"b\")?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn swap_dir() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| sh.write_file(\"niri/config.kdl\", \"a\"))\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.write_file(\"niri2/config.kdl\", \"b\")?;\n                sh.remove_path(\"niri\")?;\n                cmd!(sh, \"mv niri2 niri\").run()?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn swap_dir_link() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri2/config.kdl\", \"a\")?;\n                cmd!(sh, \"ln -s niri2 niri\").run()\n            })\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                sh.write_file(\"niri3/config.kdl\", \"b\")?;\n                sh.remove_path(\"niri\")?;\n                cmd!(sh, \"ln -s niri3 niri\").run()?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn change_included_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri/config.kdl\", \"include \\\"colors.kdl\\\"\")?;\n                sh.write_file(\"niri/colors.kdl\", \"// Colors\")\n            })\n            .assert_initial(\"include \\\"colors.kdl\\\"\")\n            .run(|sh, test| {\n                sh.write_file(\"niri/colors.kdl\", \"// Updated colors\")?;\n                test.assert_changed_to(\"include \\\"colors.kdl\\\"\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn remove_included_file() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri/config.kdl\", \"include \\\"colors.kdl\\\"\")?;\n                sh.write_file(\"niri/colors.kdl\", \"// Colors\")\n            })\n            .assert_initial(\"include \\\"colors.kdl\\\"\")\n            .run(|sh, test| {\n                sh.remove_path(\"niri/colors.kdl\")?;\n                test.assert_changed_to(\"include \\\"colors.kdl\\\"\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn nested_includes() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri/config.kdl\", \"include \\\"a.kdl\\\"\")?;\n                sh.write_file(\"niri/a.kdl\", \"include \\\"b.kdl\\\"\")?;\n                sh.write_file(\"niri/b.kdl\", \"// b content\")\n            })\n            .assert_initial(\"include \\\"a.kdl\\\"\")\n            .run(|sh, test| {\n                sh.write_file(\"niri/b.kdl\", \"// updated b\")?;\n                test.assert_changed_to(\"include \\\"a.kdl\\\"\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn broken_include_still_gets_watched() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup(|sh| {\n                sh.write_file(\"niri/config.kdl\", \"include \\\"colors.kdl\\\"\")?;\n                sh.write_file(\"niri/colors.kdl\", \"broken\")\n            })\n            .assert_initial(\"include \\\"colors.kdl\\\"\")\n            .run(|sh, test| {\n                sh.write_file(\"niri/colors.kdl\", \"// Fixed\")?;\n                test.assert_changed_to(\"include \\\"colors.kdl\\\"\");\n\n                Ok(())\n            })\n    }\n\n    // Important: On systems like NixOS, mtime is not kept for config files.\n    // So, this is testing that the watcher handles that correctly.\n    fn create_epoch(path: impl AsRef<Path>, content: &str) -> Result {\n        let mut file = File::create(path)?;\n        file.write_all(content.as_bytes())?;\n        file.set_times(\n            FileTimes::new()\n                .set_accessed(SystemTime::UNIX_EPOCH)\n                .set_modified(SystemTime::UNIX_EPOCH),\n        )?;\n        file.sync_all()?;\n        Ok(())\n    }\n\n    #[test]\n    fn swap_just_link() -> Result {\n        TestPath::Explicit(\"niri/config.kdl\")\n            .setup_any(|sh| {\n                let dir = sh.current_dir().join(\"niri\");\n\n                sh.create_dir(&dir)?;\n\n                create_epoch(dir.join(\"config2.kdl\"), \"a\")?;\n                create_epoch(dir.join(\"config3.kdl\"), \"b\")?;\n\n                cmd!(sh, \"ln -s config2.kdl niri/config.kdl\").run()?;\n\n                Ok(())\n            })\n            .assert_initial(\"a\")\n            .run(|sh, test| {\n                cmd!(sh, \"ln -sf config3.kdl niri/config.kdl\").run()?;\n                test.assert_changed_to(\"b\");\n\n                Ok(())\n            })\n    }\n\n    #[test]\n    fn swap_many_regular() -> Result {\n        TestPath::Regular {\n            user_path: \"user-niri/config.kdl\",\n            system_path: \"system-niri/config.kdl\",\n        }\n        .setup(|sh| sh.write_file(\"system-niri/config.kdl\", \"system config\"))\n        .assert_initial(\"system config\")\n        .run(|sh, test| {\n            sh.write_file(\"user-niri/config.kdl\", \"user config\")?;\n            test.assert_changed_to(\"user config\");\n\n            cmd!(sh, \"touch system-niri/config.kdl\").run()?;\n            test.assert_unchanged();\n\n            sh.remove_path(\"system-niri\")?;\n            test.assert_unchanged();\n\n            sh.write_file(\"system-niri/config.kdl\", \"new system config\")?;\n            test.assert_unchanged();\n\n            sh.remove_path(\"user-niri\")?;\n            test.assert_changed_to(\"new system config\");\n\n            sh.write_file(\"system-niri/config.kdl\", \"updated system config\")?;\n            test.assert_changed_to(\"updated system config\");\n\n            sh.write_file(\"user-niri/config.kdl\", \"new user config\")?;\n            test.assert_changed_to(\"new user config\");\n\n            Ok(())\n        })\n    }\n\n    #[test]\n    fn swap_many_links_regular_like_nix() -> Result {\n        TestPath::Regular {\n            user_path: \"user-niri/config.kdl\",\n            system_path: \"system-niri/config.kdl\",\n        }\n        .setup_any(|sh| {\n            let store = sh.current_dir().join(\"store\");\n\n            sh.create_dir(&store)?;\n\n            create_epoch(store.join(\"gen1\"), \"gen 1\")?;\n            create_epoch(store.join(\"gen2\"), \"gen 2\")?;\n            create_epoch(store.join(\"gen3\"), \"gen 3\")?;\n\n            sh.create_dir(\"user-niri\")?;\n            sh.create_dir(\"system-niri\")?;\n\n            Ok(())\n        })\n        .assert_initial_not_exists()\n        .run(|sh, test| {\n            let store = sh.current_dir().join(\"store\");\n            test.assert_unchanged();\n\n            cmd!(sh, \"ln -s {store}/gen1 user-niri/config.kdl\").run()?;\n            test.assert_changed_to(\"gen 1\");\n\n            cmd!(sh, \"ln -s {store}/gen2 system-niri/config.kdl\").run()?;\n            test.assert_unchanged();\n\n            cmd!(sh, \"unlink user-niri/config.kdl\").run()?;\n            test.assert_changed_to(\"gen 2\");\n\n            cmd!(sh, \"ln -s {store}/gen3 user-niri/config.kdl\").run()?;\n            test.assert_changed_to(\"gen 3\");\n\n            cmd!(sh, \"ln -sf {store}/gen1 system-niri/config.kdl\").run()?;\n            test.assert_unchanged();\n\n            cmd!(sh, \"unlink system-niri/config.kdl\").run()?;\n            test.assert_unchanged();\n\n            cmd!(sh, \"ln -s {store}/gen1 system-niri/config.kdl\").run()?;\n            test.assert_unchanged();\n\n            cmd!(sh, \"unlink user-niri/config.kdl\").run()?;\n            test.assert_changed_to(\"gen 1\");\n\n            Ok(())\n        })\n    }\n}\n"
  },
  {
    "path": "src/utils/xwayland/mod.rs",
    "content": "use std::os::fd::OwnedFd;\nuse std::os::unix::net::{SocketAddr, UnixListener};\n\nuse anyhow::{anyhow, ensure, Context as _};\nuse rustix::fs::{lstat, mkdir};\nuse rustix::io::Errno;\nuse rustix::process::getuid;\nuse smithay::reexports::rustix::fs::{unlink, OFlags};\nuse smithay::reexports::rustix::process::getpid;\nuse smithay::reexports::rustix::{self};\n\npub mod satellite;\n\nconst TMP_UNIX_DIR: &str = \"/tmp\";\nconst X11_TMP_UNIX_DIR: &str = \"/tmp/.X11-unix\";\n\nstruct X11Connection {\n    display_name: String,\n    // Optional because there are no abstract sockets on FreeBSD.\n    abstract_fd: Option<OwnedFd>,\n    unix_fd: OwnedFd,\n    _unix_guard: Unlink,\n    _lock_guard: Unlink,\n}\n\nstruct Unlink(String);\nimpl Drop for Unlink {\n    fn drop(&mut self) {\n        let _ = unlink(&self.0);\n    }\n}\n\n// Adapted from Mutter code:\n// https://gitlab.gnome.org/GNOME/mutter/-/blob/48.3.1/src/wayland/meta-xwayland.c?ref_type=tags#L513\nfn ensure_x11_unix_dir() -> anyhow::Result<()> {\n    match mkdir(X11_TMP_UNIX_DIR, 0o1777.into()) {\n        Ok(()) => Ok(()),\n        Err(Errno::EXIST) => {\n            ensure_x11_unix_perms().context(\"wrong X11 directory permissions\")?;\n            Ok(())\n        }\n        Err(err) => Err(err).context(\"error creating X11 directory\"),\n    }\n}\n\nfn ensure_x11_unix_perms() -> anyhow::Result<()> {\n    let x11_tmp = lstat(X11_TMP_UNIX_DIR).context(\"error checking X11 directory permissions\")?;\n    let tmp = lstat(TMP_UNIX_DIR).context(\"error checking /tmp directory permissions\")?;\n\n    ensure!(\n        x11_tmp.st_uid == tmp.st_uid || x11_tmp.st_uid == getuid().as_raw(),\n        \"wrong ownership for X11 directory\"\n    );\n    ensure!(\n        (x11_tmp.st_mode & 0o022) == 0o022,\n        \"X11 directory is not writable\"\n    );\n    ensure!(\n        (x11_tmp.st_mode & 0o1000) == 0o1000,\n        \"X11 directory is missing the sticky bit\"\n    );\n\n    Ok(())\n}\n\nfn pick_x11_display(start: u32) -> anyhow::Result<(u32, OwnedFd, Unlink)> {\n    for n in start..start + 50 {\n        let lock_path = format!(\"/tmp/.X{n}-lock\");\n        let flags = OFlags::WRONLY | OFlags::CLOEXEC | OFlags::CREATE | OFlags::EXCL;\n        let Ok(lock_fd) = rustix::fs::open(&lock_path, flags, 0o444.into()) else {\n            // FIXME: check if the target process is dead and reuse the lock.\n            continue;\n        };\n        return Ok((n, lock_fd, Unlink(lock_path)));\n    }\n\n    Err(anyhow!(\"no free X11 display found after 50 attempts\"))\n}\n\nfn bind_to_socket(addr: &SocketAddr) -> anyhow::Result<UnixListener> {\n    let listener = UnixListener::bind_addr(addr).context(\"error binding socket\")?;\n    Ok(listener)\n}\n\n#[cfg(target_os = \"linux\")]\nfn bind_to_abstract_socket(display: u32) -> anyhow::Result<UnixListener> {\n    use std::os::linux::net::SocketAddrExt;\n\n    let name = format!(\"/tmp/.X11-unix/X{display}\");\n    let addr = SocketAddr::from_abstract_name(name).unwrap();\n    bind_to_socket(&addr)\n}\n\nfn bind_to_unix_socket(display: u32) -> anyhow::Result<(UnixListener, Unlink)> {\n    let name = format!(\"/tmp/.X11-unix/X{display}\");\n    let addr = SocketAddr::from_pathname(&name).unwrap();\n    // Unlink old leftover socket if any.\n    let _ = unlink(&name);\n    let guard = Unlink(name);\n    bind_to_socket(&addr).map(|listener| (listener, guard))\n}\n\nfn open_display_sockets(\n    display: u32,\n) -> anyhow::Result<(Option<UnixListener>, UnixListener, Unlink)> {\n    #[cfg(target_os = \"linux\")]\n    let a = Some(bind_to_abstract_socket(display).context(\"error binding to abstract socket\")?);\n    #[cfg(not(target_os = \"linux\"))]\n    let a = None;\n\n    let (u, g) = bind_to_unix_socket(display).context(\"error binding to unix socket\")?;\n    Ok((a, u, g))\n}\n\nfn setup_connection() -> anyhow::Result<X11Connection> {\n    let _span = tracy_client::span!(\"open_x11_sockets\");\n\n    ensure_x11_unix_dir()?;\n\n    let mut n = 0;\n    let mut attempt = 0;\n    let (display, lock_guard, a, u, unix_guard) = loop {\n        let (display, lock_fd, lock_guard) = pick_x11_display(n)?;\n\n        // Write our PID into the lock file.\n        let pid_string = format!(\"{:>10}\\n\", getpid().as_raw_nonzero());\n        if let Err(err) = rustix::io::write(&lock_fd, pid_string.as_bytes()) {\n            return Err(err).context(\"error writing PID to X11 lock file\");\n        }\n        drop(lock_fd);\n\n        match open_display_sockets(display) {\n            Ok((a, u, g)) => {\n                break (display, lock_guard, a, u, g);\n            }\n            Err(err) => {\n                if attempt == 50 {\n                    return Err(err)\n                        .context(\"error opening X11 sockets after creating a lock file\");\n                }\n\n                n = display + 1;\n                attempt += 1;\n                continue;\n            }\n        }\n    };\n\n    let display_name = format!(\":{display}\");\n    let abstract_fd = a.map(OwnedFd::from);\n    let unix_fd = OwnedFd::from(u);\n\n    Ok(X11Connection {\n        display_name,\n        abstract_fd,\n        unix_fd,\n        _unix_guard: unix_guard,\n        _lock_guard: lock_guard,\n    })\n}\n"
  },
  {
    "path": "src/utils/xwayland/satellite.rs",
    "content": "use std::os::fd::{AsRawFd as _, BorrowedFd, OwnedFd};\nuse std::os::unix::net::UnixListener;\nuse std::os::unix::process::CommandExt as _;\nuse std::path::{Path, PathBuf};\nuse std::process::{Command, Stdio};\nuse std::thread;\n\nuse calloop::channel::Sender;\nuse calloop::generic::Generic;\nuse calloop::{Interest, Mode, PostAction, RegistrationToken};\nuse smithay::reexports::rustix::io::{fcntl_setfd, FdFlags};\n\nuse crate::niri::State;\nuse crate::utils::expand_home;\nuse crate::utils::xwayland::X11Connection;\n\npub struct Satellite {\n    x11: X11Connection,\n    abstract_token: Option<RegistrationToken>,\n    unix_token: Option<RegistrationToken>,\n    to_main: Sender<ToMain>,\n}\n\nenum ToMain {\n    SetupWatch,\n}\n\nimpl Satellite {\n    pub fn display_name(&self) -> &str {\n        &self.x11.display_name\n    }\n}\n\npub fn setup(state: &mut State) {\n    if state.niri.satellite.is_some() {\n        return;\n    }\n\n    let config = state.niri.config.borrow();\n    let xwls_config = &config.xwayland_satellite;\n    if xwls_config.off {\n        return;\n    }\n\n    if !test_ondemand(&xwls_config.path) {\n        return;\n    }\n    drop(config);\n\n    let x11 = match super::setup_connection() {\n        Ok(x11) => x11,\n        Err(err) => {\n            warn!(\"error opening X11 sockets, disabling xwayland-satellite integration: {err:?}\");\n            return;\n        }\n    };\n\n    let event_loop = &state.niri.event_loop;\n    let (to_main, rx) = calloop::channel::channel();\n    event_loop\n        .insert_source(rx, move |event, _, state| match event {\n            calloop::channel::Event::Msg(msg) => match msg {\n                ToMain::SetupWatch => setup_watch(state),\n            },\n            calloop::channel::Event::Closed => (),\n        })\n        .unwrap();\n\n    state.niri.satellite = Some(Satellite {\n        x11,\n        abstract_token: None,\n        unix_token: None,\n        to_main,\n    });\n\n    setup_watch(state);\n}\n\nfn test_ondemand(path: &str) -> bool {\n    let _span = tracy_client::span!(\"satellite::test_ondemand\");\n\n    // Expand `~` at the start.\n    let mut path = Path::new(path);\n    let expanded = expand_home(path);\n    match &expanded {\n        Ok(Some(expanded)) => path = expanded.as_ref(),\n        Ok(None) => (),\n        Err(err) => {\n            warn!(\"error expanding ~: {err:?}\");\n        }\n    }\n\n    let mut process = Command::new(path);\n    process\n        .args([\":0\", \"--test-listenfd-support\"])\n        .stdin(Stdio::null())\n        .stdout(Stdio::null())\n        .stderr(Stdio::null())\n        .env_remove(\"DISPLAY\")\n        .env_remove(\"RUST_BACKTRACE\")\n        .env_remove(\"RUST_LIB_BACKTRACE\");\n\n    let mut child = match process.spawn() {\n        Ok(child) => child,\n        Err(err) => {\n            warn!(\"error spawning xwayland-satellite at {path:?}, disabling integration: {err}\");\n            return false;\n        }\n    };\n\n    let status = match child.wait() {\n        Ok(status) => status,\n        Err(err) => {\n            warn!(\"error waiting for xwayland-satellite, disabling integration: {err}\");\n            return false;\n        }\n    };\n\n    if !status.success() {\n        warn!(\"xwayland-satellite doesn't support on-demand activation, disabling integration\");\n        return false;\n    }\n\n    true\n}\n\n// When xwayland-satellite fails to start and accept a connection on the socket, the socket will\n// keep triggering our event source, even after the X11 client quits, resulting in a busyloop of\n// trying to start xwayland-satellite. This function will clear out (accept and drop) all pending\n// connections on the socket before registering a new event source, working around this problem.\n// When the problem happens, it's very likely that xwayland-satellite won't be able to accept the\n// pending client (since it had just failed to do so), so it's fine to drop the connections.\nfn clear_out_pending_connections(fd: OwnedFd) -> OwnedFd {\n    let listener = UnixListener::from(fd);\n\n    if let Err(err) = listener.set_nonblocking(true) {\n        warn!(\"error setting X11 socket to nonblocking: {err:?}\");\n        return OwnedFd::from(listener);\n    }\n\n    while listener.accept().is_ok() {}\n\n    if let Err(err) = listener.set_nonblocking(false) {\n        warn!(\"error setting X11 socket to blocking: {err:?}\");\n    }\n\n    OwnedFd::from(listener)\n}\n\nfn setup_watch(state: &mut State) {\n    let Some(satellite) = state.niri.satellite.as_mut() else {\n        return;\n    };\n\n    let event_loop = &state.niri.event_loop;\n\n    if let Some(token) = satellite.abstract_token.take() {\n        error!(\"abstract_token must be None in setup_watch()\");\n        event_loop.remove(token);\n    }\n    if let Some(token) = satellite.unix_token.take() {\n        error!(\"unix_token must be None in setup_watch()\");\n        event_loop.remove(token);\n    }\n\n    if let Some(fd) = &satellite.x11.abstract_fd {\n        let fd = fd.try_clone().unwrap();\n        let fd = clear_out_pending_connections(fd);\n        let source = Generic::new(fd, Interest::READ, Mode::Level);\n        let token = event_loop\n            .insert_source(source, move |_, _, state| {\n                if let Some(satellite) = &mut state.niri.satellite {\n                    // Remove the other source.\n                    if let Some(token) = satellite.unix_token.take() {\n                        state.niri.event_loop.remove(token);\n                    }\n                    // Clear this source.\n                    satellite.abstract_token = None;\n\n                    debug!(\"connection to X11 abstract socket; spawning xwayland-satellite\");\n                    let path = state.niri.config.borrow().xwayland_satellite.path.clone();\n                    spawn(path, satellite);\n                }\n                Ok(PostAction::Remove)\n            })\n            .unwrap();\n        satellite.abstract_token = Some(token);\n    }\n\n    let fd = satellite.x11.unix_fd.try_clone().unwrap();\n    let fd = clear_out_pending_connections(fd);\n    let source = Generic::new(fd, Interest::READ, Mode::Level);\n    let token = event_loop\n        .insert_source(source, move |_, _, state| {\n            if let Some(satellite) = &mut state.niri.satellite {\n                // Remove the other source.\n                if let Some(token) = satellite.abstract_token.take() {\n                    state.niri.event_loop.remove(token);\n                }\n                // Clear this source.\n                satellite.unix_token = None;\n\n                debug!(\"connection to X11 unix socket; spawning xwayland-satellite\");\n                let path = state.niri.config.borrow().xwayland_satellite.path.clone();\n                spawn(path, satellite);\n            }\n            Ok(PostAction::Remove)\n        })\n        .unwrap();\n    satellite.unix_token = Some(token);\n}\n\nfn spawn(path: String, xwl: &Satellite) {\n    let _span = tracy_client::span!(\"satellite::spawn\");\n\n    let abstract_fd = xwl\n        .x11\n        .abstract_fd\n        .as_ref()\n        .map(|fd| fd.try_clone().unwrap());\n    let unix_fd = xwl.x11.unix_fd.try_clone().unwrap();\n    let to_main = xwl.to_main.clone();\n\n    // Expand `~` at the start.\n    let mut path = PathBuf::from(path);\n    let expanded = expand_home(&path);\n    match expanded {\n        Ok(Some(expanded)) => path = expanded,\n        Ok(None) => (),\n        Err(err) => {\n            warn!(\"error expanding ~: {err:?}\");\n        }\n    }\n\n    let mut process = Command::new(&path);\n    process.arg(&xwl.x11.display_name).env_remove(\"DISPLAY\");\n\n    // We don't want it spamming the niri output.\n    process\n        .env_remove(\"RUST_BACKTRACE\")\n        .env_remove(\"RUST_LIB_BACKTRACE\");\n    process\n        .stdin(Stdio::null())\n        .stdout(Stdio::null())\n        .stderr(Stdio::null());\n\n    unsafe { process.pre_exec(crate::utils::signals::unblock_all) };\n\n    // Spawning and waiting takes some milliseconds, so do it in a thread.\n    let res = thread::Builder::new()\n        .name(\"Xwl-s Spawner\".to_owned())\n        .spawn(move || {\n            spawn_and_wait(&path, process, abstract_fd, unix_fd);\n\n            // Once xwayland-satellite crashes or fails to spawn, re-establish our X11 socket watch\n            // to try again next time.\n            let _ = to_main.send(ToMain::SetupWatch);\n        });\n\n    if let Err(err) = res {\n        warn!(\"error spawning a thread to spawn xwayland-satellite: {err:?}\");\n        let _ = xwl.to_main.send(ToMain::SetupWatch);\n    }\n}\n\nfn spawn_and_wait(\n    path: &Path,\n    mut process: Command,\n    abstract_fd: Option<OwnedFd>,\n    unix_fd: OwnedFd,\n) {\n    let abstract_raw = abstract_fd.as_ref().map(|fd| fd.as_raw_fd());\n    let unix_raw = unix_fd.as_raw_fd();\n\n    process.arg(\"-listenfd\").arg(unix_raw.to_string());\n\n    if let Some(abstract_raw) = abstract_raw {\n        process.arg(\"-listenfd\").arg(abstract_raw.to_string());\n    }\n\n    unsafe {\n        process.pre_exec(move || {\n            // We're about to exec xwl-s; perfect time to clear CLOEXEC on the file descriptors\n            // that we want to pass it.\n\n            // We're not dropping these until after spawn().\n            let unix_fd = BorrowedFd::borrow_raw(unix_raw);\n            fcntl_setfd(unix_fd, FdFlags::empty())?;\n\n            if let Some(abstract_raw) = abstract_raw {\n                let abstract_fd = BorrowedFd::borrow_raw(abstract_raw);\n                fcntl_setfd(abstract_fd, FdFlags::empty())?;\n            }\n\n            Ok(())\n        })\n    };\n\n    let mut child = {\n        let _span = tracy_client::span!();\n        match process.spawn() {\n            Ok(child) => child,\n            Err(err) => {\n                warn!(\"error spawning {path:?}: {err:?}\");\n                return;\n            }\n        }\n    };\n\n    // The process spawned, we can drop our fds.\n    drop(abstract_fd);\n    drop(unix_fd);\n\n    let status = match child.wait() {\n        Ok(status) => status,\n        Err(err) => {\n            warn!(\"error waiting for xwayland-satellite: {err:?}\");\n            return;\n        }\n    };\n\n    // This is most likely a crash, hence warn!().\n    warn!(\"xwayland-satellite exited with: {status}\");\n}\n"
  },
  {
    "path": "src/window/mapped.rs",
    "content": "use std::cell::{Cell, Ref, RefCell};\nuse std::time::Duration;\n\nuse niri_config::{Color, CornerRadius, GradientInterpolation, WindowRule};\nuse smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;\nuse smithay::backend::renderer::element::Kind;\nuse smithay::backend::renderer::gles::GlesRenderer;\nuse smithay::desktop::space::SpaceElement as _;\nuse smithay::desktop::{PopupManager, Window};\nuse smithay::output::{self, Output};\nuse smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;\nuse smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;\nuse smithay::reexports::wayland_server::Resource as _;\nuse smithay::utils::{Logical, Point, Rectangle, Scale, Serial, Size, Transform};\nuse smithay::wayland::compositor::{remove_pre_commit_hook, with_states, HookId, SurfaceData};\nuse smithay::wayland::seat::WaylandFocus;\nuse smithay::wayland::shell::xdg::{\n    SurfaceCachedState, ToplevelCachedState, ToplevelConfigure, ToplevelSurface,\n    XdgToplevelSurfaceData,\n};\nuse wayland_backend::server::Credentials;\n\nuse super::{ResolvedWindowRules, WindowRef};\nuse crate::handlers::KdeDecorationsModeState;\nuse crate::layout::{\n    ConfigureIntent, InteractiveResizeData, LayoutElement, LayoutElementRenderElement,\n    LayoutElementRenderSnapshot, SizingMode,\n};\nuse crate::niri_render_elements;\nuse crate::render_helpers::border::BorderRenderElement;\nuse crate::render_helpers::offscreen::OffscreenData;\nuse crate::render_helpers::renderer::NiriRenderer;\nuse crate::render_helpers::snapshot::RenderSnapshot;\nuse crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};\nuse crate::render_helpers::surface::{\n    push_elements_from_surface_tree, render_snapshot_from_surface_tree,\n};\nuse crate::render_helpers::{BakedBuffer, RenderTarget};\nuse crate::utils::id::IdCounter;\nuse crate::utils::transaction::Transaction;\nuse crate::utils::{\n    get_credentials_for_surface, send_scale_transform, update_tiled_state,\n    with_toplevel_last_uncommitted_configure, with_toplevel_role, with_toplevel_role_and_current,\n    ResizeEdge,\n};\n\n#[derive(Debug)]\npub struct Mapped {\n    pub window: Window,\n\n    /// Unique ID of this `Mapped`.\n    id: MappedId,\n\n    /// Credentials of the process that created the Wayland connection.\n    credentials: Option<Credentials>,\n\n    /// Pre-commit hook that we have on all mapped toplevel surfaces.\n    pre_commit_hook: HookId,\n\n    /// Up-to-date rules.\n    rules: ResolvedWindowRules,\n\n    /// Whether the window rules need to be recomputed.\n    ///\n    /// This is not used in all cases; for example, app ID and title changes recompute the rules\n    /// immediately, rather than setting this flag.\n    need_to_recompute_rules: bool,\n\n    /// Whether this window needs a configure this loop cycle.\n    ///\n    /// Certain Wayland requests require a configure in response, like un/fullscreen.\n    needs_configure: bool,\n\n    /// Whether this window needs a frame callback.\n    ///\n    /// We set this after sending a configure to give invisible windows a chance to respond to\n    /// resizes immediately, without waiting for a 1 second throttled callback.\n    needs_frame_callback: bool,\n\n    /// Data of the offscreen element rendered in place of this window.\n    ///\n    /// If `None`, then the window is not offscreened.\n    offscreen_data: RefCell<Option<OffscreenData>>,\n\n    /// Whether this has an urgent indicator.\n    is_urgent: bool,\n\n    /// Whether this window has the keyboard focus.\n    is_focused: bool,\n\n    /// Whether this window is the active window in its column.\n    is_active_in_column: bool,\n\n    /// Whether this window is floating.\n    is_floating: bool,\n\n    /// Whether this window is a target of a window cast.\n    is_window_cast_target: bool,\n\n    /// Whether this window should ignore opacity set through window rules.\n    ignore_opacity_window_rule: bool,\n\n    /// Buffer to draw instead of the window when it should be blocked out.\n    block_out_buffer: RefCell<SolidColorBuffer>,\n\n    /// Whether the next configure should be animated, if the configured state changed.\n    animate_next_configure: bool,\n\n    /// Serials of commits that should be animated.\n    animate_serials: Vec<Serial>,\n\n    /// Snapshot right before an animated commit, without popups.\n    animation_snapshot: Option<LayoutElementRenderSnapshot>,\n\n    /// State for the logic to request a size once (for floating windows).\n    request_size_once: Option<RequestSizeOnce>,\n\n    /// Transaction that the next configure should take part in, if any.\n    transaction_for_next_configure: Option<Transaction>,\n\n    /// Pending transactions that have not been added as blockers for this window yet.\n    pending_transactions: Vec<(Serial, Transaction)>,\n\n    /// State of an ongoing interactive resize.\n    interactive_resize: Option<InteractiveResize>,\n\n    /// Last time interactive resize was started.\n    ///\n    /// Used for double-resize-click tracking.\n    last_interactive_resize_start: Cell<Option<(Duration, ResizeEdge)>>,\n\n    /// Whether this window is in windowed (fake) fullscreen.\n    ///\n    /// In this mode, the underlying window is told that it's fullscreen, while keeping it as\n    /// a regular, non-fullscreen tile.\n    is_windowed_fullscreen: bool,\n\n    /// Whether this window is pending to go to windowed (fake) fullscreen.\n    ///\n    /// Several places in the layout code assume that is_fullscreen() can flip only on a commit.\n    /// Which is something that we do want to flip when changing is_windowed_fullscreen. Flipping\n    /// it right away would mean remembering to call layout.update_window() after any operation\n    /// that may change is_windowed_fullscreen, which is quite tricky and error-prone, especially\n    /// for deeply nested operations.\n    ///\n    /// It's also not clear what's the best way to go about it. Ideally we'd wait for configure ack\n    /// and commit before \"committing\" to is_windowed_fullscreen, however, since it's not real\n    /// Wayland state, we may end up with no Wayland state change to configure at all.\n    ///\n    /// For example: when the window is in real fullscreen, but its non-fullscreen size matches\n    /// its fullscreen size. Then turning on is_windowed_fullscreen will both keep the\n    /// fullscreen state, and keep the size (since it matches), resulting in no configure.\n    ///\n    /// So we work around this by emulating a configure-ack/commit cycle through\n    /// is_pending_windowed_fullscreen and uncommitted_windowed_fullscreen. We ensure we send\n    /// actual configures in all cases through needs_configure. This can result in unnecessary\n    /// configures (like in the example above), but in most cases there will be a configure\n    /// anyway to change the Fullscreen state and/or the size. What this gives us is being able\n    /// to synchronize our windowed fullscreen state to the real window updates to avoid any\n    /// flickering.\n    is_pending_windowed_fullscreen: bool,\n\n    /// Pending windowed fullscreen updates.\n    ///\n    /// These have been \"sent\" to the window in form of configures, but the window hadn't committed\n    /// in response yet.\n    uncommitted_windowed_fullscreen: Vec<(Serial, bool)>,\n\n    /// Whether this window is maximized.\n    ///\n    /// We have to track this ourselves in addition to the Maximized toplevel state in order to\n    /// support windowed fullscreen, since in windowed fullscreen the toplevel state is always\n    /// Fullscreen. So we need this variable to be able to report accurate sizing mode and pending\n    /// sizing mode.\n    is_maximized: bool,\n\n    /// Whether this window is pending to be maximized.\n    ///\n    /// We have to track this ourselves due to windowed fullscreen.\n    is_pending_maximized: bool,\n\n    /// Pending maximized updates.\n    ///\n    /// These have been \"sent\" to the window in form of configures, but the window hadn't committed\n    /// in response yet.\n    uncommitted_maximized: Vec<(Serial, bool)>,\n\n    /// Most recent monotonic time when the window had the focus.\n    focus_timestamp: Option<Duration>,\n}\n\nniri_render_elements! {\n    WindowCastRenderElements<R> => {\n        Layout = LayoutElementRenderElement<R>,\n        // Blocked-out window with rounded corners.\n        Border = BorderRenderElement,\n    }\n}\n\nstatic MAPPED_ID_COUNTER: IdCounter = IdCounter::new();\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct MappedId(u64);\n\nimpl MappedId {\n    pub fn next() -> MappedId {\n        MappedId(MAPPED_ID_COUNTER.next())\n    }\n\n    pub fn get(self) -> u64 {\n        self.0\n    }\n}\n\n/// Interactive resize state.\n#[derive(Debug)]\nenum InteractiveResize {\n    /// The resize is ongoing.\n    Ongoing(InteractiveResizeData),\n    /// The resize has stopped and we're waiting to send the last configure.\n    WaitingForLastConfigure(InteractiveResizeData),\n    /// We had sent the last resize configure and are waiting for the corresponding commit.\n    WaitingForLastCommit {\n        data: InteractiveResizeData,\n        serial: Serial,\n    },\n}\n\nimpl InteractiveResize {\n    fn data(&self) -> InteractiveResizeData {\n        match self {\n            InteractiveResize::Ongoing(data) => *data,\n            InteractiveResize::WaitingForLastConfigure(data) => *data,\n            InteractiveResize::WaitingForLastCommit { data, .. } => *data,\n        }\n    }\n}\n\n/// Request-size-once logic state.\n#[derive(Debug, Clone, Copy)]\nenum RequestSizeOnce {\n    /// Waiting for configure to be sent with the requested size.\n    WaitingForConfigure,\n    /// Waiting for the window to commit in response to the configure.\n    WaitingForCommit(Serial),\n    /// When configuring, use the current window size.\n    UseWindowSize,\n}\n\nimpl Mapped {\n    pub fn new(window: Window, rules: ResolvedWindowRules, hook: HookId) -> Self {\n        let surface = window.wl_surface().expect(\"no X11 support\");\n        let credentials = get_credentials_for_surface(&surface);\n\n        let mut rv = Self {\n            window,\n            id: MappedId::next(),\n            credentials,\n            pre_commit_hook: hook,\n            rules,\n            need_to_recompute_rules: false,\n            needs_configure: false,\n            needs_frame_callback: false,\n            offscreen_data: RefCell::new(None),\n            is_urgent: false,\n            is_focused: false,\n            is_active_in_column: true,\n            is_floating: false,\n            is_window_cast_target: false,\n            ignore_opacity_window_rule: false,\n            block_out_buffer: RefCell::new(SolidColorBuffer::new((0., 0.), [0., 0., 0., 1.])),\n            animate_next_configure: false,\n            animate_serials: Vec::new(),\n            animation_snapshot: None,\n            request_size_once: None,\n            transaction_for_next_configure: None,\n            pending_transactions: Vec::new(),\n            interactive_resize: None,\n            last_interactive_resize_start: Cell::new(None),\n            is_windowed_fullscreen: false,\n            is_pending_windowed_fullscreen: false,\n            uncommitted_windowed_fullscreen: Vec::new(),\n            is_maximized: false,\n            is_pending_maximized: false,\n            uncommitted_maximized: Vec::new(),\n            focus_timestamp: None,\n        };\n\n        rv.is_maximized = rv.sizing_mode().is_maximized();\n        rv.is_pending_maximized = rv.pending_sizing_mode().is_maximized();\n\n        rv\n    }\n\n    pub fn toplevel(&self) -> &ToplevelSurface {\n        self.window.toplevel().expect(\"no X11 support\")\n    }\n\n    /// Recomputes the resolved window rules and returns whether they changed.\n    pub fn recompute_window_rules(&mut self, rules: &[WindowRule], is_at_startup: bool) -> bool {\n        self.need_to_recompute_rules = false;\n\n        let new_rules = ResolvedWindowRules::compute(rules, WindowRef::Mapped(self), is_at_startup);\n        if new_rules == self.rules {\n            return false;\n        }\n\n        // If the opacity window rule no longer makes the window semitransparent, reset the ignore\n        // flag to reduce surprises down the line.\n        if !new_rules.opacity.is_some_and(|o| o < 1.) {\n            self.ignore_opacity_window_rule = false;\n        }\n\n        self.rules = new_rules;\n        true\n    }\n\n    pub fn recompute_window_rules_if_needed(\n        &mut self,\n        rules: &[WindowRule],\n        is_at_startup: bool,\n    ) -> bool {\n        if !self.need_to_recompute_rules {\n            return false;\n        }\n\n        self.recompute_window_rules(rules, is_at_startup)\n    }\n\n    pub fn set_needs_configure(&mut self) {\n        self.needs_configure = true;\n    }\n\n    pub fn id(&self) -> MappedId {\n        self.id\n    }\n\n    pub fn credentials(&self) -> Option<&Credentials> {\n        self.credentials.as_ref()\n    }\n\n    pub fn offscreen_data(&self) -> Ref<'_, Option<OffscreenData>> {\n        self.offscreen_data.borrow()\n    }\n\n    pub fn is_focused(&self) -> bool {\n        self.is_focused\n    }\n\n    pub fn is_active_in_column(&self) -> bool {\n        self.is_active_in_column\n    }\n\n    pub fn is_floating(&self) -> bool {\n        self.is_floating\n    }\n\n    pub fn is_window_cast_target(&self) -> bool {\n        self.is_window_cast_target\n    }\n\n    pub fn toggle_ignore_opacity_window_rule(&mut self) {\n        self.ignore_opacity_window_rule = !self.ignore_opacity_window_rule;\n    }\n\n    pub fn set_is_focused(&mut self, is_focused: bool) {\n        if self.is_focused == is_focused {\n            return;\n        }\n\n        self.is_focused = is_focused;\n        self.is_urgent = false;\n        self.need_to_recompute_rules = true;\n    }\n\n    pub fn set_is_window_cast_target(&mut self, value: bool) {\n        if self.is_window_cast_target == value {\n            return;\n        }\n\n        self.is_window_cast_target = value;\n        self.need_to_recompute_rules = true;\n    }\n\n    /// Renders a snapshot of the window without popups.\n    fn render_snapshot(&self, renderer: &mut GlesRenderer) -> LayoutElementRenderSnapshot {\n        let _span = tracy_client::span!(\"Mapped::render_snapshot\");\n\n        let size = self.size().to_f64();\n\n        let mut buffer = self.block_out_buffer.borrow_mut();\n        buffer.resize(size);\n        let blocked_out_contents = vec![BakedBuffer {\n            buffer: buffer.clone(),\n            location: Point::from((0., 0.)),\n            src: None,\n            dst: None,\n        }];\n\n        let buf_pos = self.window.geometry().loc.upscale(-1).to_f64();\n\n        let mut contents = vec![];\n\n        let surface = self.toplevel().wl_surface();\n        render_snapshot_from_surface_tree(renderer, surface, buf_pos, &mut contents);\n\n        RenderSnapshot {\n            contents,\n            blocked_out_contents,\n            block_out_from: self.rules().block_out_from,\n            size,\n            texture: Default::default(),\n            blocked_out_texture: Default::default(),\n        }\n    }\n\n    pub fn should_animate_commit(&mut self, commit_serial: Serial) -> bool {\n        let mut should_animate = false;\n        self.animate_serials.retain_mut(|serial| {\n            if commit_serial.is_no_older_than(serial) {\n                should_animate = true;\n                false\n            } else {\n                true\n            }\n        });\n        should_animate\n    }\n\n    pub fn store_animation_snapshot(&mut self, renderer: &mut GlesRenderer) {\n        self.animation_snapshot = Some(self.render_snapshot(renderer));\n    }\n\n    pub fn take_pending_transaction(&mut self, commit_serial: Serial) -> Option<Transaction> {\n        let mut rv = None;\n\n        // Pending transactions are appended in order by serial, so we can loop from the start\n        // until we hit a serial that is too new.\n        while let Some((serial, _)) = self.pending_transactions.first() {\n            // In this loop, we will complete the transaction corresponding to the commit, as well\n            // as all transactions corresponding to previous serials. This can happen when we\n            // request resizes too quickly, and the surface only responds to the last one.\n            //\n            // Note that in this case, completing the previous transactions can result in an\n            // inconsistent visual state, if another window is waiting for this window to assume a\n            // specific size (in a previous transaction), which is now different (in this commit).\n            //\n            // However, there isn't really a good way to deal with that. We cannot cancel any\n            // transactions because we need to keep sending frame callbacks, and cancelling a\n            // transaction will make the corresponding frame callbacks get lost, and the window\n            // will hang.\n            //\n            // This is why resize throttling (implemented separately) is important: it prevents\n            // visually inconsistent states by way of never having more than one transaction in\n            // flight.\n            if commit_serial.is_no_older_than(serial) {\n                let (_, transaction) = self.pending_transactions.remove(0);\n                // Previous transaction is dropped here, signaling completion.\n                rv = Some(transaction);\n            } else {\n                break;\n            }\n        }\n\n        rv\n    }\n\n    pub fn last_interactive_resize_start(&self) -> &Cell<Option<(Duration, ResizeEdge)>> {\n        &self.last_interactive_resize_start\n    }\n\n    pub fn render_for_screen_cast<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        scale: Scale<f64>,\n        push: &mut dyn FnMut(WindowCastRenderElements<R>),\n    ) {\n        let bbox = self.window.bbox_with_popups().to_physical_precise_up(scale);\n\n        let has_border_shader = BorderRenderElement::has_shader(renderer);\n        let rules = self.rules();\n        let radius = rules.geometry_corner_radius.unwrap_or_default();\n        let window_size = self\n            .size()\n            .to_f64()\n            .to_physical_precise_round(scale)\n            .to_logical(scale);\n        let radius = radius.fit_to(window_size.w as f32, window_size.h as f32);\n        let location = self.window.geometry().loc.to_f64() - bbox.loc.to_logical(scale);\n\n        let use_border = |elem| {\n            if let LayoutElementRenderElement::SolidColor(elem) = &elem {\n                // In this branch we're rendering a blocked-out window with a solid color. We need\n                // to render it with a rounded corner shader even if clip_to_geometry is false,\n                // because in this case we're assuming that the unclipped window CSD already has\n                // corners rounded to the user-provided radius, so our blocked-out rendering should\n                // match that radius.\n                if radius != CornerRadius::default() && has_border_shader {\n                    let geo = elem.geo();\n                    return BorderRenderElement::new(\n                        geo.size,\n                        Rectangle::from_size(geo.size),\n                        GradientInterpolation::default(),\n                        Color::from_color32f(elem.color()),\n                        Color::from_color32f(elem.color()),\n                        0.,\n                        Rectangle::from_size(geo.size),\n                        0.,\n                        radius,\n                        scale.x as f32,\n                        1.,\n                    )\n                    .with_location(geo.loc)\n                    .into();\n                }\n            }\n\n            WindowCastRenderElements::from(elem)\n        };\n\n        self.render(\n            renderer,\n            location,\n            scale,\n            1.,\n            RenderTarget::Screencast,\n            &mut |elem| push(use_border(elem)),\n        );\n    }\n\n    pub fn get_focus_timestamp(&self) -> Option<Duration> {\n        self.focus_timestamp\n    }\n\n    pub fn set_focus_timestamp(&mut self, timestamp: Duration) {\n        self.focus_timestamp.replace(timestamp);\n    }\n\n    pub fn send_frame<T, F>(\n        &mut self,\n        output: &Output,\n        time: T,\n        throttle: Option<Duration>,\n        mut primary_scan_out_output: F,\n    ) where\n        T: Into<Duration>,\n        F: FnMut(&WlSurface, &SurfaceData) -> Option<Output> + Copy,\n    {\n        let needs_frame_callback = self.needs_frame_callback;\n        self.needs_frame_callback = false;\n\n        let should_send = move |surface: &WlSurface, states: &SurfaceData| {\n            // Let primary_scan_out_output() run its logic and update internal state.\n            if let Some(output) = primary_scan_out_output(surface, states) {\n                return Some(output);\n            }\n\n            // Send unconditionally to all surfaces if the window needs a surface callback.\n            needs_frame_callback.then(|| output.clone())\n        };\n        self.window.send_frame(output, time, throttle, should_send);\n    }\n\n    pub fn update_tiled_state(&self, prefer_no_csd: bool) {\n        update_tiled_state(self.toplevel(), prefer_no_csd, self.rules.tiled_state);\n    }\n\n    pub fn is_windowed_fullscreen(&self) -> bool {\n        self.is_windowed_fullscreen\n    }\n\n    pub fn set_urgent(&mut self, urgent: bool) {\n        if self.is_focused && urgent {\n            return;\n        }\n\n        let changed = self.is_urgent != urgent;\n        self.is_urgent = urgent;\n        self.need_to_recompute_rules |= changed;\n    }\n\n    pub fn is_urgent(&self) -> bool {\n        self.is_urgent\n    }\n}\n\nimpl Drop for Mapped {\n    fn drop(&mut self) {\n        remove_pre_commit_hook(self.toplevel().wl_surface(), self.pre_commit_hook.clone());\n    }\n}\n\nimpl LayoutElement for Mapped {\n    type Id = Window;\n\n    fn id(&self) -> &Self::Id {\n        &self.window\n    }\n\n    fn size(&self) -> Size<i32, Logical> {\n        self.window.geometry().size\n    }\n\n    fn buf_loc(&self) -> Point<i32, Logical> {\n        Point::from((0, 0)) - self.window.geometry().loc\n    }\n\n    fn is_in_input_region(&self, point: Point<f64, Logical>) -> bool {\n        let surface_local = point + self.window.geometry().loc.to_f64();\n        self.window.is_in_input_region(&surface_local)\n    }\n\n    fn render_normal<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        scale: Scale<f64>,\n        alpha: f32,\n        target: RenderTarget,\n        push: &mut dyn FnMut(LayoutElementRenderElement<R>),\n    ) {\n        if target.should_block_out(self.rules.block_out_from) {\n            let mut buffer = self.block_out_buffer.borrow_mut();\n            buffer.resize(self.window.geometry().size.to_f64());\n            let elem =\n                SolidColorRenderElement::from_buffer(&buffer, location, alpha, Kind::Unspecified);\n            push(elem.into());\n        } else {\n            let buf_pos = location - self.window.geometry().loc.to_f64();\n            let surface = self.toplevel().wl_surface();\n            let mut push = |elem: WaylandSurfaceRenderElement<R>| push(elem.into());\n            push_elements_from_surface_tree(\n                renderer,\n                surface,\n                buf_pos.to_physical_precise_round(scale),\n                scale,\n                alpha,\n                Kind::ScanoutCandidate,\n                &mut push,\n            )\n        }\n    }\n\n    fn render_popups<R: NiriRenderer>(\n        &self,\n        renderer: &mut R,\n        location: Point<f64, Logical>,\n        scale: Scale<f64>,\n        alpha: f32,\n        target: RenderTarget,\n        push: &mut dyn FnMut(LayoutElementRenderElement<R>),\n    ) {\n        if target.should_block_out(self.rules.block_out_from) {\n            return;\n        }\n\n        let buf_pos = location - self.window.geometry().loc.to_f64();\n        let surface = self.toplevel().wl_surface();\n        let mut push = |elem: WaylandSurfaceRenderElement<R>| push(elem.into());\n        for (popup, popup_offset) in PopupManager::popups_for_surface(surface) {\n            let offset = self.window.geometry().loc + popup_offset - popup.geometry().loc;\n\n            push_elements_from_surface_tree(\n                renderer,\n                popup.wl_surface(),\n                (buf_pos + offset.to_f64()).to_physical_precise_round(scale),\n                scale,\n                alpha,\n                Kind::ScanoutCandidate,\n                &mut push,\n            );\n        }\n    }\n\n    fn request_size(\n        &mut self,\n        size: Size<i32, Logical>,\n        mode: SizingMode,\n        animate: bool,\n        transaction: Option<Transaction>,\n    ) {\n        // Going into real fullscreen resets windowed fullscreen.\n        if mode == SizingMode::Fullscreen {\n            self.is_pending_windowed_fullscreen = false;\n\n            if self.is_windowed_fullscreen {\n                // Make sure we receive a commit to update self.is_windowed_fullscreen to false\n                // later on.\n                self.needs_configure = true;\n            }\n        }\n\n        self.is_pending_maximized = mode == SizingMode::Maximized;\n        if self.is_maximized != self.is_pending_maximized {\n            // Make sure we receive a commit to update self.is_maximized later on.\n            self.needs_configure = true;\n        }\n\n        let changed = self.toplevel().with_pending_state(|state| {\n            let changed = state.size != Some(size);\n            state.size = Some(size);\n\n            if mode.is_fullscreen() || self.is_pending_windowed_fullscreen {\n                state.states.set(xdg_toplevel::State::Fullscreen);\n                state.states.unset(xdg_toplevel::State::Maximized);\n            } else if mode.is_maximized() {\n                state.states.unset(xdg_toplevel::State::Fullscreen);\n                state.states.set(xdg_toplevel::State::Maximized);\n            } else {\n                state.states.unset(xdg_toplevel::State::Fullscreen);\n                state.states.unset(xdg_toplevel::State::Maximized);\n            }\n\n            changed\n        });\n\n        if changed && animate {\n            self.animate_next_configure = true;\n        }\n\n        self.request_size_once = None;\n\n        // Store the transaction regardless of whether the size changed. This is because with 3+\n        // windows in a column, the size may change among windows 1 and 2 and then right away among\n        // windows 2 and 3, and we want all windows 1, 2 and 3 to use the last transaction, rather\n        // than window 1 getting stuck with the previous transaction that is immediately released\n        // by 2.\n        if let Some(transaction) = transaction {\n            self.transaction_for_next_configure = Some(transaction);\n        }\n    }\n\n    fn request_size_once(&mut self, size: Size<i32, Logical>, animate: bool) {\n        // Assume that when calling this function, the window is going floating, so it can no\n        // longer participate in any transactions with other windows.\n        self.transaction_for_next_configure = None;\n\n        self.is_pending_maximized = false;\n        if self.is_maximized != self.is_pending_maximized {\n            // Make sure we receive a commit to update self.is_maximized later on.\n            self.needs_configure = true;\n        }\n\n        // If our last requested size already matches the size we want to request-once, clear the\n        // size request right away. However, we must also check if we're unfullscreening, because\n        // in that case the window itself will restore its previous size upon receiving a (0, 0)\n        // configure, whereas what we potentially want is to unfullscreen the window into its\n        // fullscreen size.\n        let already_sent = with_toplevel_last_uncommitted_configure(self.toplevel(), |configure| {\n            let ToplevelConfigure { state, serial } = configure?;\n\n            let same_size = state.size.unwrap_or_default() == size;\n            let has_fullscreen = state.states.contains(xdg_toplevel::State::Fullscreen);\n            let same_fullscreen = has_fullscreen == self.is_pending_windowed_fullscreen;\n            let has_maximized = state.states.contains(xdg_toplevel::State::Maximized);\n            let same_maximized = !has_maximized;\n            (same_size && same_fullscreen && same_maximized).then_some(*serial)\n        });\n\n        if let Some(serial) = already_sent {\n            let current_serial = with_states(self.toplevel().wl_surface(), |states| {\n                states\n                    .cached_state\n                    .get::<ToplevelCachedState>()\n                    .current()\n                    .last_acked\n                    .as_ref()\n                    .map(|c| c.serial)\n            });\n            if let Some(current_serial) = current_serial {\n                // God this triple negative...\n                if !current_serial.is_no_older_than(&serial) {\n                    // We have already sent a request for the new size, but the surface has not\n                    // committed in response yet, so we will wait for that commit.\n                    self.request_size_once = Some(RequestSizeOnce::WaitingForCommit(serial));\n                } else {\n                    // We have already sent a request for the new size, and the surface has\n                    // committed in response, so we will start using the current size right away.\n                    self.request_size_once = Some(RequestSizeOnce::UseWindowSize);\n                }\n            } else {\n                warn!(\"no current serial; did the surface not ack the initial configure?\");\n                self.request_size_once = Some(RequestSizeOnce::UseWindowSize);\n            };\n            return;\n        }\n\n        let changed = self.toplevel().with_pending_state(|state| {\n            let changed = state.size != Some(size);\n            state.size = Some(size);\n            if !self.is_pending_windowed_fullscreen {\n                state.states.unset(xdg_toplevel::State::Fullscreen);\n            }\n            state.states.unset(xdg_toplevel::State::Maximized);\n            changed\n        });\n\n        if changed && animate {\n            self.animate_next_configure = true;\n        }\n\n        self.request_size_once = Some(RequestSizeOnce::WaitingForConfigure);\n    }\n\n    fn min_size(&self) -> Size<i32, Logical> {\n        let min_size = with_states(self.toplevel().wl_surface(), |state| {\n            let mut guard = state.cached_state.get::<SurfaceCachedState>();\n            guard.current().min_size\n        });\n\n        self.rules.apply_min_size(min_size)\n    }\n\n    fn max_size(&self) -> Size<i32, Logical> {\n        let max_size = with_states(self.toplevel().wl_surface(), |state| {\n            let mut guard = state.cached_state.get::<SurfaceCachedState>();\n            guard.current().max_size\n        });\n\n        self.rules.apply_max_size(max_size)\n    }\n\n    fn is_wl_surface(&self, wl_surface: &WlSurface) -> bool {\n        self.toplevel().wl_surface() == wl_surface\n    }\n\n    fn set_preferred_scale_transform(&self, scale: output::Scale, transform: Transform) {\n        self.window.with_surfaces(|surface, data| {\n            send_scale_transform(surface, data, scale, transform);\n        });\n    }\n\n    fn has_ssd(&self) -> bool {\n        let toplevel = self.toplevel();\n        let mode = self\n            .toplevel()\n            .with_committed_state(|current| current.and_then(|s| s.decoration_mode));\n\n        match mode {\n            Some(zxdg_toplevel_decoration_v1::Mode::ServerSide) => true,\n            // Check KDE decorations when XDG are not in use.\n            None => with_states(toplevel.wl_surface(), |states| {\n                states\n                    .data_map\n                    .get::<KdeDecorationsModeState>()\n                    .map(KdeDecorationsModeState::is_server)\n                    == Some(true)\n            }),\n            _ => false,\n        }\n    }\n\n    fn output_enter(&self, output: &Output) {\n        let overlap = Rectangle::from_size(Size::from((i32::MAX, i32::MAX)));\n        self.window.output_enter(output, overlap)\n    }\n\n    fn output_leave(&self, output: &Output) {\n        self.window.output_leave(output)\n    }\n\n    fn set_offscreen_data(&self, data: Option<OffscreenData>) {\n        let Some(data) = data else {\n            self.offscreen_data.replace(None);\n            return;\n        };\n\n        let mut offscreen_data = self.offscreen_data.borrow_mut();\n        match &mut *offscreen_data {\n            None => {\n                *offscreen_data = Some(data);\n            }\n            Some(existing) => {\n                // Replace the id, amend existing element states. This is necessary to handle\n                // multiple layers of offscreen (e.g. resize animation + alpha animation).\n                existing.id = data.id;\n                existing.states.states.extend(data.states.states);\n            }\n        }\n    }\n\n    fn is_urgent(&self) -> bool {\n        self.is_urgent\n    }\n\n    fn set_activated(&mut self, active: bool) {\n        let changed = self.toplevel().with_pending_state(|state| {\n            if active {\n                state.states.set(xdg_toplevel::State::Activated)\n            } else {\n                state.states.unset(xdg_toplevel::State::Activated)\n            }\n        });\n        self.need_to_recompute_rules |= changed;\n    }\n\n    fn set_active_in_column(&mut self, active: bool) {\n        let changed = self.is_active_in_column != active;\n        self.is_active_in_column = active;\n        self.need_to_recompute_rules |= changed;\n    }\n\n    fn set_floating(&mut self, floating: bool) {\n        let changed = self.is_floating != floating;\n        self.is_floating = floating;\n        self.need_to_recompute_rules |= changed;\n    }\n\n    fn set_bounds(&self, bounds: Size<i32, Logical>) {\n        self.toplevel().with_pending_state(|state| {\n            state.bounds = Some(bounds);\n        });\n    }\n\n    fn configure_intent(&self) -> ConfigureIntent {\n        let _span =\n            trace_span!(\"configure_intent\", surface = ?self.toplevel().wl_surface().id()).entered();\n\n        if self.needs_configure {\n            trace!(\"the window needs_configure\");\n            return ConfigureIntent::ShouldSend;\n        }\n\n        with_toplevel_role_and_current(self.toplevel(), |attributes, current_committed| {\n            if let Some(server_pending) = &attributes.server_pending {\n                let current_server = attributes.current_server_state();\n                if *server_pending != current_server {\n                    // Something changed. Check if the only difference is the size, and if the\n                    // current server size matches the current committed size.\n                    let mut current_server_same_size = current_server.clone();\n                    current_server_same_size.size = server_pending.size;\n                    if current_server_same_size == *server_pending {\n                        // Only the size changed. Check if the window committed our previous size\n                        // request.\n                        let Some(current_committed) = current_committed else {\n                            error!(\"mapped must have had initial commit\");\n                            return ConfigureIntent::ShouldSend;\n                        };\n\n                        if current_committed.size == current_server.size {\n                            // The window had committed for our previous size change, so we can\n                            // change the size again.\n                            trace!(\n                                \"current size matches server size: {:?}\",\n                                current_committed.size\n                            );\n                            ConfigureIntent::CanSend\n                        } else {\n                            // The window had not committed for our previous size change yet. Since\n                            // nothing else changed, do not send the new size request yet. This\n                            // throttling is done because some clients do not batch size requests,\n                            // leading to bad behavior with very fast input devices (i.e. a 1000 Hz\n                            // mouse). This throttling also helps interactive resize transactions\n                            // preserve visual consistency.\n                            trace!(\"throttling resize\");\n                            ConfigureIntent::Throttled\n                        }\n                    } else {\n                        // Something else changed other than the size; send it.\n                        trace!(\"something changed other than the size\");\n                        ConfigureIntent::ShouldSend\n                    }\n                } else {\n                    // Nothing changed since the last configure.\n                    ConfigureIntent::NotNeeded\n                }\n            } else {\n                // Nothing changed since the last configure.\n                ConfigureIntent::NotNeeded\n            }\n        })\n    }\n\n    fn send_pending_configure(&mut self) {\n        let toplevel = self.toplevel();\n        let _span =\n            trace_span!(\"send_pending_configure\", surface = ?toplevel.wl_surface().id()).entered();\n\n        // If the window needs a configure, send it regardless.\n        let has_pending_changes = self.needs_configure\n            || with_toplevel_role(self.toplevel(), |role| {\n                // Check for pending changes manually to account for RequestSizeOnce::UseWindowSize.\n                if role.server_pending.is_none() {\n                    return false;\n                }\n\n                let current_server_size = role.current_server_state().size;\n                let server_pending = role.server_pending.as_mut().unwrap();\n\n                // With UseWindowSize, we do not consider size-only changes, because we will\n                // request the current window size and do not expect it to actually change.\n                if let Some(RequestSizeOnce::UseWindowSize) = self.request_size_once {\n                    server_pending.size = current_server_size;\n                }\n\n                let server_pending = role.server_pending.as_ref().unwrap();\n                *server_pending != role.current_server_state()\n            });\n\n        if has_pending_changes {\n            // If needed, replace the pending size with the current window size.\n            if let Some(RequestSizeOnce::UseWindowSize) = self.request_size_once {\n                let size = self.window.geometry().size;\n                toplevel.with_pending_state(|state| {\n                    state.size = Some(size);\n                });\n            }\n\n            let serial = toplevel.send_configure();\n            trace!(?serial, \"sending configure\");\n\n            self.needs_configure = false;\n\n            // Send the window a frame callback unconditionally to let it respond to size changes\n            // and such immediately, even when it's hidden. This especially matters for cases like\n            // tabbed columns which compute their width based on all windows in the column, even\n            // hidden ones.\n            self.needs_frame_callback = true;\n\n            if self.animate_next_configure {\n                self.animate_serials.push(serial);\n            }\n\n            if let Some(transaction) = self.transaction_for_next_configure.take() {\n                self.pending_transactions.push((serial, transaction));\n            }\n\n            self.interactive_resize = match self.interactive_resize.take() {\n                Some(InteractiveResize::WaitingForLastConfigure(data)) => {\n                    Some(InteractiveResize::WaitingForLastCommit { data, serial })\n                }\n                x => x,\n            };\n\n            if let Some(RequestSizeOnce::WaitingForConfigure) = self.request_size_once {\n                self.request_size_once = Some(RequestSizeOnce::WaitingForCommit(serial));\n            }\n\n            // If is_pending_windowed_fullscreen changed compared to the last value that we \"sent\"\n            // to the window, store the configure serial.\n            let last_sent_windowed_fullscreen = self\n                .uncommitted_windowed_fullscreen\n                .last()\n                .map(|(_, value)| *value)\n                .unwrap_or(self.is_windowed_fullscreen);\n            if last_sent_windowed_fullscreen != self.is_pending_windowed_fullscreen {\n                self.uncommitted_windowed_fullscreen\n                    .push((serial, self.is_pending_windowed_fullscreen));\n            }\n\n            // If is_pending_maximized changed compared to the last value that we \"sent\" to the\n            // window, store the configure serial.\n            let last_sent_maximized = self\n                .uncommitted_maximized\n                .last()\n                .map(|(_, value)| *value)\n                .unwrap_or(self.is_maximized);\n            if last_sent_maximized != self.is_pending_maximized {\n                self.uncommitted_maximized\n                    .push((serial, self.is_pending_maximized));\n            }\n        } else {\n            self.interactive_resize = match self.interactive_resize.take() {\n                // We probably started and stopped resizing in the same loop cycle without anything\n                // changing.\n                Some(InteractiveResize::WaitingForLastConfigure { .. }) => None,\n                x => x,\n            };\n        }\n\n        self.animate_next_configure = false;\n        self.transaction_for_next_configure = None;\n    }\n\n    fn sizing_mode(&self) -> SizingMode {\n        if self.is_windowed_fullscreen {\n            return if self.is_maximized {\n                SizingMode::Maximized\n            } else {\n                SizingMode::Normal\n            };\n        }\n\n        self.toplevel().with_committed_state(|state| {\n            // This must always be Some() for mapped windows. However, this function is called on\n            // the code path when removing a just-unmapped window in the commit handler, at which\n            // point state is already None.\n            let Some(state) = state else {\n                return SizingMode::Normal;\n            };\n\n            if state.states.contains(xdg_toplevel::State::Fullscreen) {\n                SizingMode::Fullscreen\n            } else if state.states.contains(xdg_toplevel::State::Maximized) {\n                SizingMode::Maximized\n            } else {\n                SizingMode::Normal\n            }\n        })\n    }\n\n    fn pending_sizing_mode(&self) -> SizingMode {\n        if self.is_pending_windowed_fullscreen {\n            return if self.is_pending_maximized {\n                SizingMode::Maximized\n            } else {\n                SizingMode::Normal\n            };\n        }\n\n        self.toplevel().with_pending_state(|state| {\n            if state.states.contains(xdg_toplevel::State::Fullscreen) {\n                SizingMode::Fullscreen\n            } else if state.states.contains(xdg_toplevel::State::Maximized) {\n                SizingMode::Maximized\n            } else {\n                SizingMode::Normal\n            }\n        })\n    }\n\n    fn is_ignoring_opacity_window_rule(&self) -> bool {\n        self.ignore_opacity_window_rule\n    }\n\n    fn requested_size(&self) -> Option<Size<i32, Logical>> {\n        self.toplevel().with_pending_state(|state| state.size)\n    }\n\n    fn expected_size(&self) -> Option<Size<i32, Logical>> {\n        // We can only use current size if it's not maximized or fullscreen.\n        let current_size = (self.sizing_mode().is_normal()).then(|| self.window.geometry().size);\n\n        // Check if we should be using the current window size.\n        //\n        // This branch can be useful (give different result than the logic below) in this example\n        // case:\n        //\n        // 1. We request_size_once a size change.\n        // 2. We send a second configure requesting a state change.\n        // 3. The window acks and commits-to the first configure but not the second, with a\n        //    different size.\n        //\n        // In this case self.request_size_once will already flip to UseWindowSize and this branch\n        // will return the window's own new size, but the logic below would see an uncommitted size\n        // change and return our size.\n        if let Some(RequestSizeOnce::UseWindowSize) = self.request_size_once {\n            return current_size;\n        }\n\n        let pending = with_states(self.toplevel().wl_surface(), |states| {\n            let role = states\n                .data_map\n                .get::<XdgToplevelSurfaceData>()\n                .unwrap()\n                .lock()\n                .unwrap();\n\n            // If we have a server-pending size change that we haven't sent yet, use that size.\n            let server_pending = role.server_pending.as_ref()?;\n\n            let current_server = role.current_server_state();\n            if server_pending.size != current_server.size {\n                return Some((\n                    server_pending.size.unwrap_or_default(),\n                    server_pending\n                        .states\n                        .contains(xdg_toplevel::State::Fullscreen),\n                    server_pending\n                        .states\n                        .contains(xdg_toplevel::State::Maximized),\n                ));\n            }\n\n            None\n        })\n        .or_else(|| {\n            with_toplevel_last_uncommitted_configure(self.toplevel(), |configure| {\n                // If we have a sent-but-not-committed-to size, use that.\n                let ToplevelConfigure { state, .. } = configure?;\n\n                Some((\n                    state.size.unwrap_or_default(),\n                    state.states.contains(xdg_toplevel::State::Fullscreen),\n                    state.states.contains(xdg_toplevel::State::Maximized),\n                ))\n            })\n        });\n\n        if let Some((mut size, fullscreen, maximized)) = pending {\n            // If the pending change is maximized or fullscreen, we can't use that size.\n            //\n            // Pending windowed fullscreen is good (means not real fullscreen), unless it's also\n            // pending maximized (means maximized windowed fullscreen, so maximized size, bad).\n            if maximized\n                || (fullscreen\n                    && (!self.is_pending_windowed_fullscreen || self.is_pending_maximized))\n            {\n                return None;\n            }\n\n            // If some component of the pending size is zero, substitute it with the current window\n            // size. But only if the current size is not fullscreen.\n            if size.w == 0 {\n                size.w = current_size?.w;\n            }\n            if size.h == 0 {\n                size.h = current_size?.h;\n            }\n\n            Some(size)\n        } else {\n            // No pending size, return the current size if it's non-fullscreen.\n            current_size\n        }\n    }\n\n    fn is_pending_windowed_fullscreen(&self) -> bool {\n        self.is_pending_windowed_fullscreen\n    }\n\n    fn request_windowed_fullscreen(&mut self, value: bool) {\n        if self.is_pending_windowed_fullscreen == value {\n            return;\n        }\n\n        self.is_pending_windowed_fullscreen = value;\n\n        // Set the fullscreen state to match.\n        //\n        // When going from windowed to real fullscreen, we'll use request_size() which will set the\n        // fullscreen state back.\n        self.toplevel().with_pending_state(|state| {\n            if value {\n                state.states.set(xdg_toplevel::State::Fullscreen);\n                state.states.unset(xdg_toplevel::State::Maximized);\n            } else {\n                state.states.unset(xdg_toplevel::State::Fullscreen);\n\n                if self.is_pending_maximized {\n                    state.states.set(xdg_toplevel::State::Maximized);\n                }\n            }\n        });\n\n        // Make sure we receive a commit later to update self.is_windowed_fullscreen.\n        self.needs_configure = true;\n    }\n\n    fn is_child_of(&self, parent: &Self) -> bool {\n        self.toplevel().parent().as_ref() == Some(parent.toplevel().wl_surface())\n    }\n\n    fn refresh(&self) {\n        self.window.refresh();\n    }\n\n    fn rules(&self) -> &ResolvedWindowRules {\n        &self.rules\n    }\n\n    fn take_animation_snapshot(&mut self) -> Option<LayoutElementRenderSnapshot> {\n        self.animation_snapshot.take()\n    }\n\n    fn set_interactive_resize(&mut self, data: Option<InteractiveResizeData>) {\n        self.toplevel().with_pending_state(|state| {\n            if data.is_some() {\n                state.states.set(xdg_toplevel::State::Resizing);\n            } else {\n                state.states.unset(xdg_toplevel::State::Resizing);\n            }\n        });\n\n        if let Some(data) = data {\n            self.interactive_resize = Some(InteractiveResize::Ongoing(data));\n        } else {\n            self.interactive_resize = match self.interactive_resize.take() {\n                Some(InteractiveResize::Ongoing(data)) => {\n                    Some(InteractiveResize::WaitingForLastConfigure(data))\n                }\n                x => x,\n            }\n        }\n    }\n\n    fn cancel_interactive_resize(&mut self) {\n        self.set_interactive_resize(None);\n        self.interactive_resize = None;\n    }\n\n    fn interactive_resize_data(&self) -> Option<InteractiveResizeData> {\n        Some(self.interactive_resize.as_ref()?.data())\n    }\n\n    fn on_commit(&mut self, commit_serial: Serial) {\n        if let Some(InteractiveResize::WaitingForLastCommit { serial, .. }) =\n            &self.interactive_resize\n        {\n            if commit_serial.is_no_older_than(serial) {\n                self.interactive_resize = None;\n            }\n        }\n\n        if let Some(RequestSizeOnce::WaitingForCommit(serial)) = &self.request_size_once {\n            if commit_serial.is_no_older_than(serial) {\n                self.request_size_once = Some(RequestSizeOnce::UseWindowSize);\n            }\n        }\n\n        // \"Commit\" our \"acked\" pending windowed fullscreen state.\n        self.uncommitted_windowed_fullscreen\n            .retain_mut(|(serial, value)| {\n                if commit_serial.is_no_older_than(serial) {\n                    self.is_windowed_fullscreen = *value;\n                    false\n                } else {\n                    true\n                }\n            });\n\n        // \"Commit\" our \"acked\" pending maximized state.\n        self.uncommitted_maximized.retain_mut(|(serial, value)| {\n            if commit_serial.is_no_older_than(serial) {\n                self.is_maximized = *value;\n                false\n            } else {\n                true\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "src/window/mod.rs",
    "content": "use std::cmp::{max, min};\n\nuse niri_config::utils::MergeWith as _;\nuse niri_config::window_rule::{Match, WindowRule};\nuse niri_config::{\n    BlockOutFrom, BorderRule, CornerRadius, FloatingPosition, PresetSize, ShadowRule,\n    TabIndicatorRule,\n};\nuse niri_ipc::ColumnDisplay;\nuse smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;\nuse smithay::utils::{Logical, Size};\nuse smithay::wayland::compositor::with_states;\nuse smithay::wayland::shell::xdg::{\n    SurfaceCachedState, ToplevelSurface, XdgToplevelSurfaceRoleAttributes,\n};\n\nuse crate::utils::with_toplevel_role;\n\npub mod mapped;\npub use mapped::Mapped;\n\npub mod unmapped;\npub use unmapped::{InitialConfigureState, Unmapped};\n\n/// Reference to a mapped or unmapped window.\n#[derive(Debug, Clone, Copy)]\npub enum WindowRef<'a> {\n    Unmapped(&'a Unmapped),\n    Mapped(&'a Mapped),\n}\n\n/// Rules fully resolved for a window.\n#[derive(Debug, Default, PartialEq, Clone)]\npub struct ResolvedWindowRules {\n    /// Default width for this window.\n    ///\n    /// - `None`: unset (global default should be used).\n    /// - `Some(None)`: set to empty (window picks its own width).\n    /// - `Some(Some(width))`: set to a particular width.\n    pub default_width: Option<Option<PresetSize>>,\n\n    /// Default height for this window.\n    ///\n    /// - `None`: unset (global default should be used).\n    /// - `Some(None)`: set to empty (window picks its own height).\n    /// - `Some(Some(height))`: set to a particular height.\n    pub default_height: Option<Option<PresetSize>>,\n\n    /// Default column display for this window.\n    pub default_column_display: Option<ColumnDisplay>,\n\n    /// Default floating position for this window.\n    pub default_floating_position: Option<FloatingPosition>,\n\n    /// Output to open this window on.\n    pub open_on_output: Option<String>,\n\n    /// Workspace to open this window on.\n    pub open_on_workspace: Option<String>,\n\n    /// Whether the window should open full-width.\n    pub open_maximized: Option<bool>,\n\n    /// Whether the window should open maximized to edges (true maximized).\n    pub open_maximized_to_edges: Option<bool>,\n\n    /// Whether the window should open fullscreen.\n    pub open_fullscreen: Option<bool>,\n\n    /// Whether the window should open floating.\n    pub open_floating: Option<bool>,\n\n    /// Whether the window should open focused.\n    pub open_focused: Option<bool>,\n\n    /// Extra bound on the minimum window width.\n    pub min_width: Option<u16>,\n    /// Extra bound on the minimum window height.\n    pub min_height: Option<u16>,\n    /// Extra bound on the maximum window width.\n    pub max_width: Option<u16>,\n    /// Extra bound on the maximum window height.\n    pub max_height: Option<u16>,\n\n    /// Focus ring overrides.\n    pub focus_ring: BorderRule,\n    /// Window border overrides.\n    pub border: BorderRule,\n    /// Shadow overrides.\n    pub shadow: ShadowRule,\n    /// Tab indicator overrides.\n    pub tab_indicator: TabIndicatorRule,\n\n    /// Whether or not to draw the border with a solid background.\n    ///\n    /// `None` means using the SSD heuristic.\n    pub draw_border_with_background: Option<bool>,\n\n    /// Extra opacity to draw this window with.\n    pub opacity: Option<f32>,\n\n    /// Corner radius to assume this window has.\n    pub geometry_corner_radius: Option<CornerRadius>,\n\n    /// Whether to clip this window to its geometry, including the corner radius.\n    pub clip_to_geometry: Option<bool>,\n\n    /// Whether to bob this window up and down.\n    pub baba_is_float: Option<bool>,\n\n    /// Whether to block out this window from certain render targets.\n    pub block_out_from: Option<BlockOutFrom>,\n\n    /// Whether to enable VRR on this window's primary output if it is on-demand.\n    pub variable_refresh_rate: Option<bool>,\n\n    /// Multiplier for all scroll events sent to this window.\n    pub scroll_factor: Option<f64>,\n\n    /// Override whether to set the Tiled xdg-toplevel state on the window.\n    pub tiled_state: Option<bool>,\n}\n\nimpl<'a> WindowRef<'a> {\n    pub fn toplevel(self) -> &'a ToplevelSurface {\n        match self {\n            WindowRef::Unmapped(unmapped) => unmapped.toplevel(),\n            WindowRef::Mapped(mapped) => mapped.toplevel(),\n        }\n    }\n\n    pub fn is_focused(self) -> bool {\n        match self {\n            WindowRef::Unmapped(_) => false,\n            WindowRef::Mapped(mapped) => mapped.is_focused(),\n        }\n    }\n\n    pub fn is_urgent(self) -> bool {\n        match self {\n            WindowRef::Unmapped(_) => false,\n            WindowRef::Mapped(mapped) => mapped.is_urgent(),\n        }\n    }\n\n    pub fn is_active_in_column(self) -> bool {\n        match self {\n            WindowRef::Unmapped(_) => true,\n            WindowRef::Mapped(mapped) => mapped.is_active_in_column(),\n        }\n    }\n\n    pub fn is_floating(self) -> bool {\n        match self {\n            // FIXME: This means you cannot set initial configure rules based on is-floating. I'm\n            // not sure there's a good way to support it, since this matcher makes a cycle with the\n            // open-floating rule.\n            //\n            // That said, I don't think there are a lot of useful initial configure properties you\n            // may want to set through an is-floating matcher? Like, if you're configuring a\n            // specific window to open as floating, you can also set those properties in that same\n            // window rule, rather than relying on a different is-floating rule.\n            WindowRef::Unmapped(_) => false,\n            WindowRef::Mapped(mapped) => mapped.is_floating(),\n        }\n    }\n\n    pub fn is_window_cast_target(self) -> bool {\n        match self {\n            WindowRef::Unmapped(_) => false,\n            WindowRef::Mapped(mapped) => mapped.is_window_cast_target(),\n        }\n    }\n}\n\nimpl ResolvedWindowRules {\n    pub fn compute(rules: &[WindowRule], window: WindowRef, is_at_startup: bool) -> Self {\n        let _span = tracy_client::span!(\"ResolvedWindowRules::compute\");\n\n        let mut resolved = ResolvedWindowRules::default();\n\n        with_toplevel_role(window.toplevel(), |role| {\n            // Ensure server_pending like in Smithay's with_pending_state().\n            if role.server_pending.is_none() {\n                role.server_pending = Some(role.current_server_state().clone());\n            }\n\n            let mut open_on_output = None;\n            let mut open_on_workspace = None;\n\n            for rule in rules {\n                let matches = |m: &Match| {\n                    if let Some(at_startup) = m.at_startup {\n                        if at_startup != is_at_startup {\n                            return false;\n                        }\n                    }\n\n                    window_matches(window, role, m)\n                };\n\n                if !(rule.matches.is_empty() || rule.matches.iter().any(matches)) {\n                    continue;\n                }\n\n                if rule.excludes.iter().any(matches) {\n                    continue;\n                }\n\n                if let Some(x) = rule.default_column_width {\n                    resolved.default_width = Some(x.0);\n                }\n\n                if let Some(x) = rule.default_window_height {\n                    resolved.default_height = Some(x.0);\n                }\n\n                if let Some(x) = rule.default_column_display {\n                    resolved.default_column_display = Some(x);\n                }\n\n                if let Some(x) = rule.default_floating_position {\n                    resolved.default_floating_position = Some(x);\n                }\n\n                if let Some(x) = rule.open_on_output.as_deref() {\n                    open_on_output = Some(x);\n                }\n\n                if let Some(x) = rule.open_on_workspace.as_deref() {\n                    open_on_workspace = Some(x);\n                }\n\n                if let Some(x) = rule.open_maximized {\n                    resolved.open_maximized = Some(x);\n                }\n\n                if let Some(x) = rule.open_maximized_to_edges {\n                    resolved.open_maximized_to_edges = Some(x);\n                }\n\n                if let Some(x) = rule.open_fullscreen {\n                    resolved.open_fullscreen = Some(x);\n                }\n\n                if let Some(x) = rule.open_floating {\n                    resolved.open_floating = Some(x);\n                }\n\n                if let Some(x) = rule.open_focused {\n                    resolved.open_focused = Some(x);\n                }\n\n                if let Some(x) = rule.min_width {\n                    resolved.min_width = Some(x);\n                }\n                if let Some(x) = rule.min_height {\n                    resolved.min_height = Some(x);\n                }\n                if let Some(x) = rule.max_width {\n                    resolved.max_width = Some(x);\n                }\n                if let Some(x) = rule.max_height {\n                    resolved.max_height = Some(x);\n                }\n\n                resolved.focus_ring.merge_with(&rule.focus_ring);\n                resolved.border.merge_with(&rule.border);\n                resolved.shadow.merge_with(&rule.shadow);\n                resolved.tab_indicator.merge_with(&rule.tab_indicator);\n\n                if let Some(x) = rule.draw_border_with_background {\n                    resolved.draw_border_with_background = Some(x);\n                }\n                if let Some(x) = rule.opacity {\n                    resolved.opacity = Some(x);\n                }\n                if let Some(x) = rule.geometry_corner_radius {\n                    resolved.geometry_corner_radius = Some(x);\n                }\n                if let Some(x) = rule.clip_to_geometry {\n                    resolved.clip_to_geometry = Some(x);\n                }\n                if let Some(x) = rule.baba_is_float {\n                    resolved.baba_is_float = Some(x);\n                }\n                if let Some(x) = rule.block_out_from {\n                    resolved.block_out_from = Some(x);\n                }\n                if let Some(x) = rule.variable_refresh_rate {\n                    resolved.variable_refresh_rate = Some(x);\n                }\n                if let Some(x) = rule.scroll_factor {\n                    resolved.scroll_factor = Some(x.0);\n                }\n                if let Some(x) = rule.tiled_state {\n                    resolved.tiled_state = Some(x);\n                }\n            }\n\n            resolved.open_on_output = open_on_output.map(|x| x.to_owned());\n            resolved.open_on_workspace = open_on_workspace.map(|x| x.to_owned());\n        });\n\n        resolved\n    }\n\n    pub fn apply_min_size(&self, min_size: Size<i32, Logical>) -> Size<i32, Logical> {\n        let mut size = min_size;\n\n        if let Some(x) = self.min_width {\n            size.w = max(size.w, i32::from(x));\n        }\n        if let Some(x) = self.min_height {\n            size.h = max(size.h, i32::from(x));\n        }\n\n        size\n    }\n\n    pub fn apply_max_size(&self, max_size: Size<i32, Logical>) -> Size<i32, Logical> {\n        let mut size = max_size;\n\n        if let Some(x) = self.max_width {\n            if size.w == 0 {\n                size.w = i32::from(x);\n            } else if x > 0 {\n                size.w = min(size.w, i32::from(x));\n            }\n        }\n        if let Some(x) = self.max_height {\n            if size.h == 0 {\n                size.h = i32::from(x);\n            } else if x > 0 {\n                size.h = min(size.h, i32::from(x));\n            }\n        }\n\n        size\n    }\n\n    pub fn apply_min_max_size(\n        &self,\n        min_size: Size<i32, Logical>,\n        max_size: Size<i32, Logical>,\n    ) -> (Size<i32, Logical>, Size<i32, Logical>) {\n        let min_size = self.apply_min_size(min_size);\n        let max_size = self.apply_max_size(max_size);\n        (min_size, max_size)\n    }\n\n    pub fn compute_open_floating(&self, toplevel: &ToplevelSurface) -> bool {\n        if let Some(res) = self.open_floating {\n            return res;\n        }\n\n        // Windows with a parent (usually dialogs) open as floating by default.\n        if toplevel.parent().is_some() {\n            return true;\n        }\n\n        let (min_size, max_size) = with_states(toplevel.wl_surface(), |state| {\n            let mut guard = state.cached_state.get::<SurfaceCachedState>();\n            let current = guard.current();\n            (current.min_size, current.max_size)\n        });\n        let (min_size, max_size) = self.apply_min_max_size(min_size, max_size);\n\n        // We open fixed-height windows as floating.\n        min_size.h > 0 && min_size.h == max_size.h\n    }\n}\n\nfn window_matches(window: WindowRef, role: &XdgToplevelSurfaceRoleAttributes, m: &Match) -> bool {\n    // Must be ensured by the caller.\n    let server_pending = role.server_pending.as_ref().unwrap();\n\n    if let Some(is_focused) = m.is_focused {\n        if window.is_focused() != is_focused {\n            return false;\n        }\n    }\n\n    if let Some(is_urgent) = m.is_urgent {\n        if window.is_urgent() != is_urgent {\n            return false;\n        }\n    }\n\n    if let Some(is_active) = m.is_active {\n        // Our \"is-active\" definition corresponds to the window having a pending Activated state.\n        let pending_activated = server_pending\n            .states\n            .contains(xdg_toplevel::State::Activated);\n        if is_active != pending_activated {\n            return false;\n        }\n    }\n\n    if let Some(app_id_re) = &m.app_id {\n        let Some(app_id) = &role.app_id else {\n            return false;\n        };\n        if !app_id_re.0.is_match(app_id) {\n            return false;\n        }\n    }\n\n    if let Some(title_re) = &m.title {\n        let Some(title) = &role.title else {\n            return false;\n        };\n        if !title_re.0.is_match(title) {\n            return false;\n        }\n    }\n\n    if let Some(is_active_in_column) = m.is_active_in_column {\n        if window.is_active_in_column() != is_active_in_column {\n            return false;\n        }\n    }\n\n    if let Some(is_floating) = m.is_floating {\n        if window.is_floating() != is_floating {\n            return false;\n        }\n    }\n\n    if let Some(is_window_cast_target) = m.is_window_cast_target {\n        if window.is_window_cast_target() != is_window_cast_target {\n            return false;\n        }\n    }\n\n    true\n}\n"
  },
  {
    "path": "src/window/unmapped.rs",
    "content": "use niri_config::PresetSize;\nuse smithay::desktop::Window;\nuse smithay::output::Output;\nuse smithay::wayland::shell::xdg::ToplevelSurface;\nuse smithay::wayland::xdg_activation::XdgActivationTokenData;\n\nuse super::ResolvedWindowRules;\n\n#[derive(Debug)]\npub struct Unmapped {\n    pub window: Window,\n    pub state: InitialConfigureState,\n    /// Activation token, if one was used on this unmapped window.\n    pub activation_token_data: Option<XdgActivationTokenData>,\n}\n\n#[allow(clippy::large_enum_variant)]\n#[derive(Debug)]\npub enum InitialConfigureState {\n    /// The window has not been initially configured yet.\n    NotConfigured {\n        /// Whether the window requested to be fullscreened, and the requested output, if any.\n        wants_fullscreen: Option<Option<Output>>,\n\n        /// Whether the window requested to be maximized.\n        wants_maximized: bool,\n    },\n    /// The window has been configured.\n    Configured {\n        /// Up-to-date rules.\n        ///\n        /// We start tracking window rules when sending the initial configure, since they don't\n        /// affect anything before that.\n        rules: ResolvedWindowRules,\n\n        /// Resolved scrolling default width for this window.\n        ///\n        /// `None` means that the window will pick its own width.\n        width: Option<PresetSize>,\n\n        /// Resolved scrolling default height for this window.\n        ///\n        /// `None` means that the window will pick its own height.\n        height: Option<PresetSize>,\n\n        /// Resolved floating default width for this window.\n        ///\n        /// `None` means that the window will pick its own width.\n        floating_width: Option<PresetSize>,\n\n        /// Resolved floating default height for this window.\n        ///\n        /// `None` means that the window will pick its own height.\n        floating_height: Option<PresetSize>,\n\n        /// Whether the window should open full-width.\n        is_full_width: bool,\n\n        /// Output to open this window on.\n        ///\n        /// This can be `None` in cases like:\n        ///\n        /// - There are no outputs connected.\n        /// - This is a dialog with a parent, and there was no explicit output set, so this dialog\n        ///   should fetch the parent's current output again upon mapping.\n        output: Option<Output>,\n\n        /// Workspace to open this window on.\n        workspace_name: Option<String>,\n\n        /// Whether the window should be maximized.\n        ///\n        /// This corresponds to the window having the Maximized toplevel state. However, if the\n        /// window is also pending fullscreen, then it has the Fullscreen toplevel state, so we\n        /// need to store pending maximized elsewhere, hence this field.\n        is_pending_maximized: bool,\n    },\n}\n\nimpl Unmapped {\n    /// Wraps a newly created window that hasn't been initially configured yet.\n    pub fn new(window: Window) -> Self {\n        Self {\n            window,\n            state: InitialConfigureState::NotConfigured {\n                wants_fullscreen: None,\n                wants_maximized: false,\n            },\n            activation_token_data: None,\n        }\n    }\n\n    pub fn needs_initial_configure(&self) -> bool {\n        matches!(self.state, InitialConfigureState::NotConfigured { .. })\n    }\n\n    pub fn toplevel(&self) -> &ToplevelSurface {\n        self.window.toplevel().expect(\"no X11 support\")\n    }\n}\n"
  },
  {
    "path": "typos.toml",
    "content": "[default]\nextend-ignore-re = [\n    \"systemd-localed\",\n]\n\n[default.extend-identifiers]\nWRONLY = \"WRONLY\"\n\n[default.extend-words]\ndatas = \"datas\"\n"
  }
]