Merge branch 'hyprwm:main' into gowno

This commit is contained in:
Honkazel 2025-11-19 14:49:19 +05:00 committed by GitHub
commit b37f8ee903
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
129 changed files with 5106 additions and 3224 deletions

View file

@ -45,6 +45,7 @@ runs:
libxkbfile \ libxkbfile \
lld \ lld \
meson \ meson \
muparser \
ninja \ ninja \
pango \ pango \
pixman \ pixman \

View file

@ -21,7 +21,7 @@ jobs:
- name: Build Hyprland - name: Build Hyprland
run: | run: |
CFLAGS=-Werror CXXFLAGS=-Werror make all CFLAGS=-Werror CXXFLAGS=-Werror make nopch
- name: Compress and package artifacts - name: Compress and package artifacts
run: | run: |
@ -41,86 +41,16 @@ jobs:
name: Build archive name: Build archive
path: Hyprland.tar.xz path: Hyprland.tar.xz
meson:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
name: "Build Hyprland with Meson (Arch)"
runs-on: ubuntu-latest
container:
image: archlinux
steps:
- name: Checkout repository actions
uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure
run: meson setup build -Ddefault_library=static
- name: Compile
run: ninja -C build
no-pch:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
name: "Build Hyprland without precompiled headers (Arch)"
runs-on: ubuntu-latest
container:
image: archlinux
steps:
- name: Checkout repository actions
uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
with:
INSTALL_XORG_PKGS: true
- name: Compile
run: make nopch
noxwayland:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
name: "Build Hyprland in pure Wayland (Arch)"
runs-on: ubuntu-latest
container:
image: archlinux
steps:
- name: Checkout repository actions
uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure
run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja
- name: Compile
run: make release
clang-format: clang-format:
permissions: read-all permissions: read-all
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
name: "Code Style (Arch)" name: "Code Style"
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: archlinux
steps: steps:
- name: Checkout repository actions - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure
run: meson setup build -Ddefault_library=static
- name: clang-format check - name: clang-format check
run: ninja -C build clang-format-check uses: jidicula/clang-format-action@v4.16.0
with:
exclude-regex: ^subprojects$

View file

@ -4,43 +4,23 @@ jobs:
clang-format: clang-format:
permissions: write-all permissions: write-all
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
name: "Code Style (Arch)" name: "Code Style"
runs-on: ubuntu-latest runs-on: ubuntu-latest
container:
image: archlinux
steps: steps:
- name: Checkout repository actions - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: Setup base
uses: ./.github/actions/setup_base
- name: Configure
run: meson setup build -Ddefault_library=static
- name: clang-format check - name: clang-format check
run: ninja -C build clang-format-check uses: jidicula/clang-format-action@v4.16.0
with:
exclude-regex: ^subprojects$
- name: clang-format apply - name: Create comment
if: ${{ failure() && github.event_name == 'pull_request' }}
run: ninja -C build clang-format
- name: Create patch
if: ${{ failure() && github.event_name == 'pull_request' }} if: ${{ failure() && github.event_name == 'pull_request' }}
run: | run: |
echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style), or directly apply this patch:' > clang-format.patch echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style).' > clang-format.patch
echo '<details>' >> clang-format.patch
echo '<summary>clang-format.patch</summary>' >> clang-format.patch
echo >> clang-format.patch
echo '```diff' >> clang-format.patch
git diff >> clang-format.patch
echo '```' >> clang-format.patch
echo >> clang-format.patch
echo '</details>' >> clang-format.patch
- name: Comment patch - name: Post comment
if: ${{ failure() && github.event_name == 'pull_request' }} if: ${{ failure() && github.event_name == 'pull_request' }}
uses: mshick/add-pr-comment@v2 uses: mshick/add-pr-comment@v2
with: with:

View file

@ -20,25 +20,13 @@ jobs:
- name: Restore and save Nix store - name: Restore and save Nix store
uses: nix-community/cache-nix-action@v6 uses: nix-community/cache-nix-action@v6
with: with:
# restore and save a cache using this key # restore and save a cache using this key (per job)
primary-key: nix-${{ runner.os }} primary-key: nix-${{ runner.os }}-${{ github.job }}
# if there's no cache hit, restore a cache by this prefix # if there's no cache hit, restore a cache by this prefix
restore-prefixes-first-match: nix-${{ runner.os }} restore-prefixes-first-match: nix-${{ runner.os }}
# collect garbage until the Nix store size (in bytes) is at most this number # collect garbage until the Nix store size (in bytes) is at most this number
# before trying to save a new cache # before trying to save a new cache
# 1G = 1073741824
gc-max-store-size-linux: 5G gc-max-store-size-linux: 5G
# do purge caches
purge: true
# purge all versions of the cache
purge-prefixes: nix-${{ runner.os }}
# created more than this number of seconds ago
purge-created: 0
# or, last accessed more than this number of seconds ago
# relative to the start of the `Post Restore and save Nix store` phase
purge-last-accessed: 0
# except any version with the key that is the same as the `primary-key`
purge-primary-key: never
- uses: cachix/cachix-action@v15 - uses: cachix/cachix-action@v15
with: with:

View file

@ -27,25 +27,13 @@ jobs:
- name: Restore and save Nix store - name: Restore and save Nix store
uses: nix-community/cache-nix-action@v6 uses: nix-community/cache-nix-action@v6
with: with:
# restore and save a cache using this key # restore and save a cache using this key (per job)
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} primary-key: nix-${{ runner.os }}-${{ github.job }}
# if there's no cache hit, restore a cache by this prefix # if there's no cache hit, restore a cache by this prefix
restore-prefixes-first-match: nix-${{ runner.os }}- restore-prefixes-first-match: nix-${{ runner.os }}
# collect garbage until the Nix store size (in bytes) is at most this number # collect garbage until the Nix store size (in bytes) is at most this number
# before trying to save a new cache # before trying to save a new cache
# 1G = 1073741824 gc-max-store-size-linux: 5G
gc-max-store-size-linux: 1G
# do purge caches
purge: true
# purge all versions of the cache
purge-prefixes: nix-${{ runner.os }}-
# created more than this number of seconds ago
purge-created: 0
# or, last accessed more than this number of seconds ago
# relative to the start of the `Post Restore and save Nix store` phase
purge-last-accessed: 0
# except any version with the key that is the same as the `primary-key`
purge-primary-key: never
- name: Update inputs - name: Update inputs
run: nix/update-inputs.sh run: nix/update-inputs.sh

View file

@ -25,25 +25,13 @@ jobs:
- name: Restore and save Nix store - name: Restore and save Nix store
uses: nix-community/cache-nix-action@v6 uses: nix-community/cache-nix-action@v6
with: with:
# restore and save a cache using this key # restore and save a cache using this key (per job)
primary-key: nix-${{ runner.os }} primary-key: nix-${{ runner.os }}-${{ github.job }}
# if there's no cache hit, restore a cache by this prefix # if there's no cache hit, restore a cache by this prefix
restore-prefixes-first-match: nix-${{ runner.os }} restore-prefixes-first-match: nix-${{ runner.os }}
# collect garbage until the Nix store size (in bytes) is at most this number # collect garbage until the Nix store size (in bytes) is at most this number
# before trying to save a new cache # before trying to save a new cache
# 1G = 1073741824
gc-max-store-size-linux: 5G gc-max-store-size-linux: 5G
# do purge caches
purge: true
# purge all versions of the cache
purge-prefixes: nix-${{ runner.os }}
# created more than this number of seconds ago
purge-created: 0
# or, last accessed more than this number of seconds ago
# relative to the start of the `Post Restore and save Nix store` phase
purge-last-accessed: 0
# except any version with the key that is the same as the `primary-key`
purge-primary-key: never
- uses: cachix/cachix-action@v15 - uses: cachix/cachix-action@v15
with: with:

View file

@ -0,0 +1,154 @@
name: AI Translation Check
on:
# pull_request_target:
# types:
# - opened
issue_comment:
types:
- created
permissions:
contents: read
pull-requests: write
issues: write
jobs:
changes:
name: Check i18n changes
if: ${{ github.event_name == 'pull_request_target' || (github.event_name == 'issue_comment' && github.event.action == 'created' && github.event.issue.pull_request != null && github.event.comment.user.login == 'vaxerski' && github.event.comment.body == 'ai, please recheck' ) }}
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v5
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
i18n:
- 'src/i18n/**'
review:
name: Review Translation
needs: changes
if: ${{ needs.changes.outputs.i18n == 'true' }}
runs-on: ubuntu-latest
env:
OPENAI_MODEL: gpt-5-mini
SYSTEM_PROMPT: |
You are a programmer and a translator. Your job is to review the attached patch for adding translation to a piece of software and make sure the submitted translation is not malicious, and that it makes sense. If the translation is not malicious, and doesn't contain obvious grammatical mistakes, say "Translation check OK". Otherwise, say "Translation check not ok" and list bad entries.
Examples of bad translations include obvious trolling (slurs, etc) or nonsense sentences. Meaningful improvements may be suggested, but if there are only minor improvements, just reply with "Translation check OK". Do not provide anything but the result and (if applicable) the bad entries or improvements.
AI_PROMPT: Translation patch below.
steps:
- name: Checkout source code
uses: actions/checkout@v5
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
i18n:
- 'src/i18n/**'
- name: Stop if i18n not changed
if: steps.changes.outputs.i18n != 'true'
run: echo "No i18n changes in this PR; skipping." && exit 0
- name: Determine PR number
id: pr
run: |
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
else
echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT"
fi
- name: Download combined PR diff
id: get_diff
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
run: |
# Get the combined diff for the entire PR
curl -sSL \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3.diff" \
"https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" \
-o pr.diff
# Compute character length
LEN=$(wc -c < pr.diff | tr -d ' ')
echo "len=$LEN" >> "$GITHUB_OUTPUT"
if [ "$LEN" -gt 25000 ]; then
echo "too_long=true" >> "$GITHUB_OUTPUT"
else
echo "too_long=false" >> "$GITHUB_OUTPUT"
fi
echo "got diff:"
cat pr.diff
- name: Comment when diff length exceeded
if: steps.get_diff.outputs.too_long == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
run: |
jq -n --arg body "Diff length exceeded, can't query API" '{body: ("AI translation check result:\n\n" + $body)}' > body.json
curl -sS -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
"https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \
--data @body.json
- name: Query OpenAI and post review
if: steps.get_diff.outputs.too_long == 'false'
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_MODEL: ${{ env.OPENAI_MODEL }}
SYSTEM_PROMPT: ${{ env.SYSTEM_PROMPT }}
AI_PROMPT: ${{ env.AI_PROMPT }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.pr.outputs.number }}
run: |
# Prepare OpenAI chat request payload (embed diff safely)
jq -n \
--arg model "$OPENAI_MODEL" \
--arg sys "$SYSTEM_PROMPT" \
--arg prompt "$AI_PROMPT" \
--rawfile diff pr.diff \
'{model:$model,
messages:[
{role:"system", content:$sys},
{role:"user", content: ($prompt + "\n\n```diff\n" + $diff + "\n```")}
]
}' > payload.json
# Call OpenAI
curl -sS https://api.openai.com/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d @payload.json > response.json
# Extract response text
COMMENT=$(jq -r '.choices[0].message.content // empty' response.json)
if [ -z "$COMMENT" ]; then
COMMENT="AI did not return a response."
fi
# If failed, add a note
ADDITIONAL_NOTE=""
if [[ "$COMMENT" == *"not ok"* ]]; then
ADDITIONAL_NOTE=$(echo -ne "\n\nPlease note this check is a guideline, not a hard requirement. It is here to help you translate. If you disagree with some points, just state that. Any typos should be fixed.")
fi
# Post the review as a PR comment
jq -n --arg body "$COMMENT" --arg note "$ADDITIONAL_NOTE" '{body: ("AI translation check result:\n\n" + $body + $note)}' > body.json
echo "CURLing https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments"
curl -sS -X POST \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Content-Type: application/json" \
"https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \
--data @body.json

View file

@ -108,7 +108,7 @@ find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.3) pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.3)
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2) pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7) pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.8.2) pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.10.2)
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.6) pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.6)
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION}) string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
@ -128,13 +128,41 @@ set(HYPRGRAPHICS_VERSION "${hyprgraphics_dep_VERSION}")
find_package(Git QUIET) find_package(Git QUIET)
# Populate variables with env vars if present
set(GIT_COMMIT_HASH "$ENV{GIT_COMMIT_HASH}")
if(NOT GIT_COMMIT_HASH)
set(GIT_COMMIT_HASH "unknown") set(GIT_COMMIT_HASH "unknown")
endif()
set(GIT_BRANCH "$ENV{GIT_BRANCH}")
if(NOT GIT_BRANCH)
set(GIT_BRANCH "unknown") set(GIT_BRANCH "unknown")
endif()
set(GIT_COMMIT_MESSAGE "$ENV{GIT_COMMIT_MESSAGE}")
if(NOT GIT_COMMIT_MESSAGE)
set(GIT_COMMIT_MESSAGE "unknown") set(GIT_COMMIT_MESSAGE "unknown")
endif()
set(GIT_COMMIT_DATE "$ENV{GIT_COMMIT_DATE}")
if(NOT GIT_COMMIT_DATE)
set(GIT_COMMIT_DATE "unknown") set(GIT_COMMIT_DATE "unknown")
endif()
set(GIT_DIRTY "$ENV{GIT_DIRTY}")
if(NOT GIT_DIRTY)
set(GIT_DIRTY "unknown") set(GIT_DIRTY "unknown")
endif()
set(GIT_TAG "$ENV{GIT_TAG}")
if(NOT GIT_TAG)
set(GIT_TAG "unknown") set(GIT_TAG "unknown")
endif()
set(GIT_COMMITS "$ENV{GIT_COMMITS}")
if(NOT GIT_COMMITS)
set(GIT_COMMITS "0") set(GIT_COMMITS "0")
endif()
if(Git_FOUND) if(Git_FOUND)
execute_process( execute_process(
@ -155,10 +183,10 @@ if(Git_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current
WORKING_DIRECTORY ${GIT_TOPLEVEL} WORKING_DIRECTORY ${GIT_TOPLEVEL}
OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%s execute_process(COMMAND sh "-c" "${GIT_EXECUTABLE} show -s --format=%s --no-show-signature | sed \"s/\\\"/\'/g\""
WORKING_DIRECTORY ${GIT_TOPLEVEL} WORKING_DIRECTORY ${GIT_TOPLEVEL}
OUTPUT_VARIABLE GIT_COMMIT_MESSAGE OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE GIT_COMMIT_MESSAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%cd --date=local execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%cd --date=local --no-show-signature
WORKING_DIRECTORY ${GIT_TOPLEVEL} WORKING_DIRECTORY ${GIT_TOPLEVEL}
OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --quiet HEAD -- execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --quiet HEAD --
@ -192,7 +220,7 @@ pkg_check_modules(
deps deps
REQUIRED REQUIRED
IMPORTED_TARGET IMPORTED_TARGET
xkbcommon xkbcommon>=1.11.0
uuid uuid
wayland-server>=1.22.90 wayland-server>=1.22.90
wayland-protocols>=1.45 wayland-protocols>=1.45
@ -205,7 +233,8 @@ pkg_check_modules(
libinput>=1.28 libinput>=1.28
gbm gbm
gio-2.0 gio-2.0
re2) re2
muparser)
find_package(hyprwayland-scanner 0.3.10 REQUIRED) find_package(hyprwayland-scanner 0.3.10 REQUIRED)
@ -483,7 +512,6 @@ add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}")
# installable assets # installable assets
file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*") file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*")
list(FILTER INSTALLABLE_ASSETS EXCLUDE REGEX "meson.build")
install(FILES ${INSTALLABLE_ASSETS} install(FILES ${INSTALLABLE_ASSETS}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr) DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)

View file

@ -1,10 +0,0 @@
globber = run_command('sh', '-c', 'find . -type f -not -name "*.build"', check: true)
files = globber.stdout().strip().split('\n')
foreach file : files
install_data(
file,
install_dir: join_paths(get_option('datadir'), 'hypr'),
install_tag: 'runtime',
)
endforeach

View file

@ -1,7 +0,0 @@
install_data(
'hyprland-portals.conf',
install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'),
install_tag: 'runtime',
)
subdir('install')

View file

@ -1,2 +0,0 @@
install_man('Hyprland.1')
install_man('hyprctl.1')

View file

@ -159,10 +159,23 @@ animations {
# uncomment all if you wish to use that. # uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0 # workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0 # workspace = f[1], gapsout:0, gapsin:0
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1] # windowrule {
# windowrule = rounding 0, floating:0, onworkspace:w[tv1] # name = no-gaps-wtv1
# windowrule = bordersize 0, floating:0, onworkspace:f[1] # match:float = false
# windowrule = rounding 0, floating:0, onworkspace:f[1] # match:workspace = w[tv1]
#
# border_size = 0
# rounding = 0
# }
#
# windowrule {
# name = no-gaps-f1
# match:float = false
# match:workspace = f[1]
#
# border_size = 0
# rounding = 0
# }
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more # See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
dwindle { dwindle {
@ -294,11 +307,25 @@ bindl = , XF86AudioPrev, exec, playerctl previous
# See https://wiki.hypr.land/Configuring/Window-Rules/ for more # See https://wiki.hypr.land/Configuring/Window-Rules/ for more
# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules # See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule # Example windowrules that are useful
# windowrule = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this. windowrule {
windowrule = suppressevent maximize, class:.* # Ignore maximize requests from all apps. You'll probably like this.
name = suppress-maximize-events
match:class = .*
suppress_event = maximize
}
windowrule {
# Fix some dragging issues with XWayland # Fix some dragging issues with XWayland
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0 name = fix-xwayland-drags
match:class = ^$
match:title = ^$
match:xwayland = true
match:float = true
match:fullscreen = false
match:pin = false
no_focus = true
}

View file

@ -1,10 +0,0 @@
install_data(
'hyprland.conf',
install_dir: join_paths(get_option('datadir'), 'hypr'),
install_tag: 'runtime',
)
install_data(
'hyprland.desktop',
install_dir: join_paths(get_option('datadir'), 'wayland-sessions'),
install_tag: 'runtime',
)

24
flake.lock generated
View file

@ -193,11 +193,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1758927902, "lastModified": 1763254292,
"narHash": "sha256-LZgMds7M94+vuMql2bERQ6LiFFdhgsEFezE4Vn+Ys3A=", "narHash": "sha256-JNgz3Fz2KMzkT7aR72wsgu/xNeJB//LSmdilh8Z/Zao=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprlang", "repo": "hyprlang",
"rev": "4dafa28d4f79877d67a7d1a654cddccf8ebf15da", "rev": "deea98d5b61d066bdc7a68163edd2c4bd28d3a6b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -261,11 +261,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762387740, "lastModified": 1763323331,
"narHash": "sha256-gQ9zJ+pUI4o+Gh4Z6jhJll7jjCSwi8ZqJIhCE2oqwhQ=", "narHash": "sha256-+Z0OfCo1MS8/aIutSAW5aJR9zTae1wz9kcJYMgpwN6M=",
"owner": "hyprwm", "owner": "hyprwm",
"repo": "hyprutils", "repo": "hyprutils",
"rev": "926689ddb9c0a8787e58c02c765a62e32d63d1f7", "rev": "0c6411851cc779d551edc89b83966696201611aa",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -299,11 +299,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1762363567, "lastModified": 1763283776,
"narHash": "sha256-YRqMDEtSMbitIMj+JLpheSz0pwEr0Rmy5mC7myl17xs=", "narHash": "sha256-Y7TDFPK4GlqrKrivOcsHG8xSGqQx3A6c+i7novT85Uk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ae814fd3904b621d8ab97418f1d0f2eb0d3716f4", "rev": "50a96edd8d0db6cc8db57dab6bb6d6ee1f3dc49a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -322,11 +322,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762441963, "lastModified": 1763319842,
"narHash": "sha256-j+rNQ119ffYUkYt2YYS6rnd6Jh/crMZmbqpkGLXaEt0=", "narHash": "sha256-YG19IyrTdnVn0l3DvcUYm85u3PaqBt6tI6VvolcuHnA=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "8e7576e79b88c16d7ee3bbd112c8d90070832885", "rev": "7275fa67fbbb75891c16d9dee7d88e58aea2d761",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -151,7 +151,6 @@
(pkgsFor.${system}) (pkgsFor.${system})
# hyprland-packages # hyprland-packages
hyprland hyprland
hyprland-with-hyprtester
hyprland-unwrapped hyprland-unwrapped
# hyprland-extras # hyprland-extras
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland

View file

@ -1,27 +0,0 @@
executable(
'hyprctl',
'main.cpp',
dependencies: [
dependency('hyprutils', version: '>= 0.1.1'),
dependency('re2', required: true)
],
install: true,
)
install_data(
'hyprctl.bash',
install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
install_tag: 'runtime',
rename: 'hyprctl',
)
install_data(
'hyprctl.fish',
install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
install_tag: 'runtime',
)
install_data(
'hyprctl.zsh',
install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
install_tag: 'runtime',
rename: '_hyprctl',
)

View file

@ -25,8 +25,8 @@ constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
Flags: Flags:
--notify | -n Send a hyprland notification for important events (including both successes and fail events). --notify | -n Send a hyprland notification confirming successful plugin load.
--notify-fail | -nn Send a hyprland notification for fail events only. Warnings/Errors trigger notifications regardless of this flag.
--help | -h Show this menu. --help | -h Show this menu.
--verbose | -v Enable too much logging. --verbose | -v Enable too much logging.
--force | -f Force an operation ignoring checks (e.g. update -f). --force | -f Force an operation ignoring checks (e.g. update -f).
@ -47,7 +47,7 @@ int main(int argc, char** argv, char** envp) {
} }
std::vector<std::string> command; std::vector<std::string> command;
bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false; bool notify = false, verbose = false, force = false, noShallow = false;
std::string customHlUrl; std::string customHlUrl;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
@ -58,7 +58,9 @@ int main(int argc, char** argv, char** envp) {
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") { } else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
notify = true; notify = true;
} else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") { } else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") {
notifyFail = notify = true; // TODO: Deprecated since v.053.0. Remove in version>0.56.0
std::println(stderr, "{}", failureString("Deprececated flag."));
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] -n flag is deprecated, see hyprpm --help.");
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") { } else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
verbose = true; verbose = true;
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") { } else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
@ -149,8 +151,9 @@ int main(int argc, char** argv, char** envp) {
if (ret2 != LOADSTATE_OK) if (ret2 != LOADSTATE_OK)
return 1; return 1;
} else if (notify) } else {
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers"); g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
}
} else if (command[0] == "enable") { } else if (command[0] == "enable") {
if (command.size() < 2) { if (command.size() < 2) {
std::println(stderr, "{}", failureString("Not enough args for enable.")); std::println(stderr, "{}", failureString("Not enough args for enable."));
@ -194,7 +197,6 @@ int main(int argc, char** argv, char** envp) {
auto ret = g_pPluginManager->ensurePluginsLoadState(force); auto ret = g_pPluginManager->ensurePluginsLoadState(force);
if (ret != LOADSTATE_OK) { if (ret != LOADSTATE_OK) {
if (notify) {
switch (ret) { switch (ret) {
case LOADSTATE_FAIL: case LOADSTATE_FAIL:
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break; case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
@ -203,10 +205,9 @@ int main(int argc, char** argv, char** envp) {
break; break;
default: break; default: break;
} }
}
return 1; return 1;
} else if (notify && !notifyFail) { } else if (notify) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins"); g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
} }
} else if (command[0] == "purge-cache") { } else if (command[0] == "purge-cache") {

View file

@ -1,32 +0,0 @@
globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
src = globber.stdout().strip().split('\n')
executable(
'hyprpm',
src,
dependencies: [
dependency('hyprutils', version: '>= 0.1.1'),
dependency('threads'),
dependency('tomlplusplus'),
dependency('glaze', method: 'cmake'),
],
install: true,
)
install_data(
'../hyprpm.bash',
install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
install_tag: 'runtime',
rename: 'hyprpm',
)
install_data(
'../hyprpm.fish',
install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
install_tag: 'runtime',
)
install_data(
'../hyprpm.zsh',
install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
install_tag: 'runtime',
rename: '_hyprpm',
)

View file

@ -11,6 +11,8 @@
#include <src/managers/input/InputManager.hpp> #include <src/managers/input/InputManager.hpp>
#include <src/managers/PointerManager.hpp> #include <src/managers/PointerManager.hpp>
#include <src/managers/input/trackpad/TrackpadGestures.hpp> #include <src/managers/input/trackpad/TrackpadGestures.hpp>
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
#include <src/Compositor.hpp> #include <src/Compositor.hpp>
#undef private #undef private
@ -245,6 +247,30 @@ static SDispatchResult keybind(std::string in) {
return {}; return {};
} }
static Desktop::Rule::CWindowRuleEffectContainer::storageType ruleIDX = 0;
//
static SDispatchResult addRule(std::string in) {
ruleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != ruleIDX)
return {.success = false, .error = "re-registering returned a different id?"};
return {};
}
static SDispatchResult checkRule(std::string in) {
if (!g_pCompositor->m_lastWindow)
return {.success = false, .error = "No window"};
if (!g_pCompositor->m_lastWindow->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
return {.success = false, .error = "No rule"};
if (g_pCompositor->m_lastWindow->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
return {.success = false, .error = "Effect isn't \"effect\""};
return {};
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle; PHANDLE = handle;
@ -255,6 +281,8 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_rule", ::addRule);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_rule", ::checkRule);
// init mouse // init mouse
g_mouse = CTestMouse::create(false); g_mouse = CTestMouse::create(false);

View file

@ -64,7 +64,7 @@ static bool startClient(SClient& client) {
// wait for window to appear // wait for window to appear
std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::this_thread::sleep_for(std::chrono::milliseconds(5000));
if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") { if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") {
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret); NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
return false; return false;
} }
@ -130,7 +130,7 @@ static bool test() {
EXPECT(sendScroll(10), true); EXPECT(sendScroll(10), true);
EXPECT(getLastDelta(client), 30); EXPECT(getLastDelta(client), 30);
EXPECT(getFromSocket("r/dispatch setprop active scrollmouse 4"), "ok"); EXPECT(getFromSocket("r/dispatch setprop active scroll_mouse 4"), "ok");
EXPECT(sendScroll(10), true); EXPECT(sendScroll(10), true);
EXPECT(getLastDelta(client), 40); EXPECT(getLastDelta(client), 40);

View file

@ -64,7 +64,7 @@ static bool startClient(SClient& client) {
// wait for window to appear // wait for window to appear
std::this_thread::sleep_for(std::chrono::milliseconds(5000)); std::this_thread::sleep_for(std::chrono::milliseconds(5000));
if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") { if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") {
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret); NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
return false; return false;
} }

View file

@ -16,6 +16,7 @@ static void testFloatClamp() {
} }
OK(getFromSocket("/keyword dwindle:force_split 2")); OK(getFromSocket("/keyword dwindle:force_split 2"));
OK(getFromSocket("/keyword monitor HEADLESS-2, addreserved, 0, 20, 0, 20"));
OK(getFromSocket("/dispatch focuswindow class:c")); OK(getFromSocket("/dispatch focuswindow class:c"));
OK(getFromSocket("/dispatch setfloating class:c")); OK(getFromSocket("/dispatch setfloating class:c"));
OK(getFromSocket("/dispatch resizewindowpixel exact 1200 900,class:c")); OK(getFromSocket("/dispatch resizewindowpixel exact 1200 900,class:c"));
@ -24,7 +25,7 @@ static void testFloatClamp() {
{ {
auto str = getFromSocket("/clients"); auto str = getFromSocket("/clients");
EXPECT_CONTAINS(str, "at: 718,178"); EXPECT_CONTAINS(str, "at: 698,158");
EXPECT_CONTAINS(str, "size: 1200,900"); EXPECT_CONTAINS(str, "size: 1200,900");
} }

View file

@ -56,82 +56,82 @@ static bool testGetprop() {
return false; return false;
} }
// animationstyle // animation
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "(unset)"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "(unset)");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": ""})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": ""})");
getFromSocket("/dispatch setprop class:kitty animationstyle teststyle"); getFromSocket("/dispatch setprop class:kitty animation teststyle");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "teststyle"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "teststyle");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": "teststyle"})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": "teststyle"})");
// maxsize // max_size
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "inf inf"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "inf inf");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [null,null]})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [null,null]})");
getFromSocket("/dispatch setprop class:kitty maxsize 200 150"); getFromSocket("/dispatch setprop class:kitty max_size 200 150");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "200 150"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "200 150");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [200,150]})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [200,150]})");
// minsize // min_size
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "20 20"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "20 20");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [20,20]})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [20,20]})");
getFromSocket("/dispatch setprop class:kitty minsize 100 50"); getFromSocket("/dispatch setprop class:kitty min_size 100 50");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "100 50"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "100 50");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [100,50]})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [100,50]})");
// alpha // opacity
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "1"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "1");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 1})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 1})");
getFromSocket("/dispatch setprop class:kitty alpha 0.3"); getFromSocket("/dispatch setprop class:kitty opacity 0.3");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "0.3"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "0.3");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 0.3})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 0.3})");
// alphainactive // opacity_inactive
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "1"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "1");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 1})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 1})");
getFromSocket("/dispatch setprop class:kitty alphainactive 0.5"); getFromSocket("/dispatch setprop class:kitty opacity_inactive 0.5");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "0.5"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "0.5");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 0.5})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 0.5})");
// alphafullscreen // opacity_fullscreen
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "1"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "1");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 1})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 1})");
getFromSocket("/dispatch setprop class:kitty alphafullscreen 0.75"); getFromSocket("/dispatch setprop class:kitty opacity_fullscreen 0.75");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "0.75"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "0.75");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 0.75})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 0.75})");
// alphaoverride // opacity_override
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "false"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": false})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": false})");
getFromSocket("/dispatch setprop class:kitty alphaoverride true"); getFromSocket("/dispatch setprop class:kitty opacity_override true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "true"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": true})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": true})");
// alphainactiveoverride // opacity_inactive_override
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "false"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": false})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": false})");
getFromSocket("/dispatch setprop class:kitty alphainactiveoverride true"); getFromSocket("/dispatch setprop class:kitty opacity_inactive_override true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "true"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": true})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": true})");
// alphafullscreenoverride // opacity_fullscreen_override
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "false"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": false})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": false})");
getFromSocket("/dispatch setprop class:kitty alphafullscreenoverride true"); getFromSocket("/dispatch setprop class:kitty opacity_fullscreen_override true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "true"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": true})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": true})");
// activebordercolor // active_border_color
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ee33ccff ee00ff99 45deg"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ee33ccff ee00ff99 45deg");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ee33ccff ee00ff99 45deg"})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ee33ccff ee00ff99 45deg"})");
getFromSocket("/dispatch setprop class:kitty activebordercolor rgb(abcdef)"); getFromSocket("/dispatch setprop class:kitty active_border_color rgb(abcdef)");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ffabcdef 0deg"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ffabcdef 0deg");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ffabcdef 0deg"})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ffabcdef 0deg"})");
// bool window properties // bool window properties
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "false"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "false");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": false})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": false})");
getFromSocket("/dispatch setprop class:kitty allowsinput true"); getFromSocket("/dispatch setprop class:kitty allows_input true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "true"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "true");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": true})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": true})");
// int window properties // int window properties
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "10"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "10");
@ -141,16 +141,16 @@ static bool testGetprop() {
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})");
// float window properties // float window properties
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "2"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "2");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 2})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 2})");
getFromSocket("/dispatch setprop class:kitty roundingpower 1.25"); getFromSocket("/dispatch setprop class:kitty rounding_power 1.25");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "1.25"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "1.25");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 1.25})"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 1.25})");
// errors // errors
EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args"); EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args");
EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animationstyle"), "window not found"); EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animation"), "window not found");
EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found");
// kill all // kill all

View file

@ -21,21 +21,24 @@ static bool testTags() {
NLog::log("{}Testing testTag tags", Colors::YELLOW); NLog::log("{}Testing testTag tags", Colors::YELLOW);
OK(getFromSocket("/keyword windowrule tag +testTag, class:tagged")); OK(getFromSocket("/keyword windowrule[tag-test-1]:tag +testTag"));
OK(getFromSocket("/keyword windowrule noshadow, tag:negative:testTag")); OK(getFromSocket("/keyword windowrule[tag-test-1]:match:class tagged"));
OK(getFromSocket("/keyword windowrule noborder, tag:testTag")); OK(getFromSocket("/keyword windowrule[tag-test-2]:match:tag negative:testTag"));
OK(getFromSocket("/keyword windowrule[tag-test-2]:no_shadow true"));
OK(getFromSocket("/keyword windowrule[tag-test-3]:match:tag testTag"));
OK(getFromSocket("/keyword windowrule[tag-test-3]:no_dim true"));
EXPECT(Tests::windowCount(), 2); EXPECT(Tests::windowCount(), 2);
OK(getFromSocket("/dispatch focuswindow class:tagged")); OK(getFromSocket("/dispatch focuswindow class:tagged"));
NLog::log("{}Testing tagged window for noborder & noshadow", Colors::YELLOW); NLog::log("{}Testing tagged window for no_dim 0 & no_shadow", Colors::YELLOW);
EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag"); EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag");
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noborder"), "true"); EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "true");
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "false"); EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "false");
NLog::log("{}Testing untagged window for noborder & noshadow", Colors::YELLOW); NLog::log("{}Testing untagged window for no_dim & no_shadow", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:untagged")); OK(getFromSocket("/dispatch focuswindow class:untagged"));
EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag"); EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag");
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noborder"), "false"); EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "true");
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "true"); EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "false");
Tests::killAllWindows(); Tests::killAllWindows();
EXPECT(Tests::windowCount(), 0); EXPECT(Tests::windowCount(), 0);

View file

@ -152,40 +152,22 @@ static bool test() {
NLog::log("{}Testing window split ratios", Colors::YELLOW); NLog::log("{}Testing window split ratios", Colors::YELLOW);
{ {
const double INITIAL_RATIO = 1.25; const double RATIO = 1.25;
const double PERCENT = RATIO / 2.0 * 100.0;
const int GAPSIN = 5; const int GAPSIN = 5;
const int GAPSOUT = 20; const int GAPSOUT = 20;
const int BORDERSIZE = 2; const int BORDERS = 2 * 2;
const int BORDERS = BORDERSIZE * 2; const int WTRIM = BORDERS + GAPSIN + GAPSOUT;
const int MONITOR_W = 1920; const int HEIGHT = 1080 - (BORDERS + (GAPSOUT * 2));
const int MONITOR_H = 1080; const int WIDTH1 = std::round(1920.0 / 2.0 * (2 - RATIO)) - WTRIM;
const int WIDTH2 = std::round(1920.0 / 2.0 * RATIO) - WTRIM;
const float totalAvailableHeight = MONITOR_H - (GAPSOUT * 2);
const int HEIGHT = std::round(totalAvailableHeight) - BORDERS;
const float availableWidthForSplit = MONITOR_W - (GAPSOUT * 2) - GAPSIN;
auto calculateFinalWidth = [&](double boxWidth, bool isLeftWindow) {
double gapLeft = isLeftWindow ? GAPSOUT : GAPSIN;
double gapRight = isLeftWindow ? GAPSIN : GAPSOUT;
return std::round(boxWidth - gapLeft - gapRight - BORDERS);
};
double geomBoxWidthA_R1 = (availableWidthForSplit * INITIAL_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0);
double geomBoxWidthB_R1 = MONITOR_W - geomBoxWidthA_R1;
const int WIDTH1 = calculateFinalWidth(geomBoxWidthB_R1, false);
const double INVERTED_RATIO = 0.75;
double geomBoxWidthA_R2 = (availableWidthForSplit * INVERTED_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0);
double geomBoxWidthB_R2 = MONITOR_W - geomBoxWidthA_R2;
const int WIDTH2 = calculateFinalWidth(geomBoxWidthB_R2, false);
const int WIDTH_A_FINAL = calculateFinalWidth(geomBoxWidthA_R2, true);
OK(getFromSocket("/keyword dwindle:default_split_ratio 1.25")); OK(getFromSocket("/keyword dwindle:default_split_ratio 1.25"));
if (!spawnKitty("kitty_B")) if (!spawnKitty("kitty_B"))
return false; return false;
NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH1, HEIGHT); NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, 100 - PERCENT);
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT)); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT));
OK(getFromSocket("/dispatch killwindow activewindow")); OK(getFromSocket("/dispatch killwindow activewindow"));
@ -197,12 +179,12 @@ static bool test() {
if (!spawnKitty("kitty_B")) if (!spawnKitty("kitty_B"))
return false; return false;
NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH2, HEIGHT); NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, PERCENT);
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH2, HEIGHT)); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH2, HEIGHT));
OK(getFromSocket("/dispatch focuswindow class:kitty_A")); OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT); NLog::log("{}Expecting kitty_A to have the same width as the previous kitty_B", Colors::YELLOW);
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH_A_FINAL, HEIGHT)); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT));
OK(getFromSocket("/keyword dwindle:default_split_ratio 1")); OK(getFromSocket("/keyword dwindle:default_split_ratio 1"));
} }
@ -246,12 +228,15 @@ static bool test() {
testSwapWindow(); testSwapWindow();
getFromSocket("/dispatch workspace 1");
NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW); NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW);
{ {
// Enable the config for testing, test max/minsize for tiled windows and centering // Enable the config for testing, test max/minsize for tiled windows and centering
OK(getFromSocket("/keyword misc:size_limits_tiled 1")); OK(getFromSocket("/keyword misc:size_limits_tiled 1"));
OK(getFromSocket("/keyword windowrule maxsize 1500 500, class:kitty_maxsize")); OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize"));
OK(getFromSocket("/keyword windowrule minsize 1200 500, class:kitty_maxsize")); OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1500 500"));
OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500"));
if (!spawnKitty("kitty_maxsize")) if (!spawnKitty("kitty_maxsize"))
return false; return false;
@ -297,29 +282,127 @@ static bool test() {
EXPECT_CONTAINS(str, "floating: 1"); EXPECT_CONTAINS(str, "floating: 1");
EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE)); EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE));
EXPECT_NOT_CONTAINS(str, "pinned: 1"); EXPECT_NOT_CONTAINS(str, "pinned: 1");
OK(getFromSocket("/keyword windowrule plugin:someplugin:variable, class:wr_kitty")); }
OK(getFromSocket("/keyword windowrule plugin:someplugin:variable 10, class:wr_kitty"));
OK(getFromSocket("/keyword windowrule workspace 1, class:wr_kitty")); OK(getFromSocket("/keyword windowrule[wr-kitty-stuff]:opacity 0.5 0.5 override"));
OK(getFromSocket("/keyword windowrule workspace special:magic, class:magic_kitty"));
{
auto str = getFromSocket("/getprop active opacity");
EXPECT_CONTAINS(str, "0.5");
}
OK(getFromSocket("/keyword windowrule[special-magic-kitty]:match:class magic_kitty"));
OK(getFromSocket("/keyword windowrule[special-magic-kitty]:workspace special:magic"));
if (!spawnKitty("magic_kitty")) if (!spawnKitty("magic_kitty"))
return false; return false;
EXPECT_CONTAINS(getFromSocket("/activewindow"), "special:magic");
{
auto str = getFromSocket("/activewindow");
EXPECT_CONTAINS(str, "special:magic");
EXPECT_NOT_CONTAINS(str, "workspace: 9"); EXPECT_NOT_CONTAINS(str, "workspace: 9");
} }
NLog::log("{}Testing faulty rules", Colors::YELLOW); if (auto str = getFromSocket("/monitors"); str.contains("magic)")) {
{ OK(getFromSocket("/dispatch togglespecialworkspace magic"));
const auto PARAM = "Invalid parameter";
const auto RULE = "Invalid value";
const auto NORULE = "no rules provided";
EXPECT_CONTAINS(getFromSocket("/keyword windowrule notarule, class:wr_kitty"), RULE)
EXPECT_CONTAINS(getFromSocket("/keyword windowrule class:wr_kitty"), NORULE)
EXPECT_CONTAINS(getFromSocket("/keyword windowrule float, class:wr_kitty, size"), PARAM)
EXPECT_CONTAINS(getFromSocket("/keyword windowrule float, classI:wr_kitty"), PARAM)
EXPECT_CONTAINS(getFromSocket("/keyword windowrule workspace:, class:wr_kitty"), NORULE)
} }
Tests::killAllWindows();
if (!spawnKitty("tag_kitty"))
return false;
{
auto str = getFromSocket("/activewindow");
EXPECT_CONTAINS(str, "floating: 1");
}
OK(getFromSocket("/reload"));
Tests::killAllWindows();
// test rules that overlap effects but don't overlap props
OK(getFromSocket("/keyword windowrule match:class overlap_kitty, border_size 0"));
OK(getFromSocket("/keyword windowrule match:fullscreen false, border_size 10"));
if (!spawnKitty("overlap_kitty"))
return false;
{
auto str = getFromSocket("/getprop active border_size");
EXPECT_CONTAINS(str, "10");
}
OK(getFromSocket("/reload"));
Tests::killAllWindows();
OK(getFromSocket("/keyword general:border_size 0"));
OK(getFromSocket("/keyword windowrule match:float true, border_size 10"));
if (!spawnKitty("border_kitty"))
return false;
{
auto str = getFromSocket("/getprop active border_size");
EXPECT_CONTAINS(str, "0");
}
OK(getFromSocket("/dispatch togglefloating"));
{
auto str = getFromSocket("/getprop active border_size");
EXPECT_CONTAINS(str, "10");
}
OK(getFromSocket("/dispatch togglefloating"));
{
auto str = getFromSocket("/getprop active border_size");
EXPECT_CONTAINS(str, "0");
}
OK(getFromSocket("/reload"));
Tests::killAllWindows();
// test expression rules
OK(getFromSocket("/keyword windowrule match:class expr_kitty, float yes, size monitor_w*0.5 monitor_h*0.5, move 20+(monitor_w*0.1) monitor_h*0.5"));
if (!spawnKitty("expr_kitty"))
return false;
{
auto str = getFromSocket("/activewindow");
EXPECT_CONTAINS(str, "floating: 1");
EXPECT_CONTAINS(str, "at: 212,540");
EXPECT_CONTAINS(str, "size: 960,540");
}
OK(getFromSocket("/reload"));
Tests::killAllWindows();
OK(getFromSocket("/dispatch plugin:test:add_rule"));
OK(getFromSocket("/reload"));
OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect"));
if (!spawnKitty("plugin_kitty"))
return false;
OK(getFromSocket("/dispatch plugin:test:check_rule"));
OK(getFromSocket("/reload"));
Tests::killAllWindows();
OK(getFromSocket("/dispatch plugin:test:add_rule"));
OK(getFromSocket("/reload"));
OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty"));
OK(getFromSocket("/keyword windowrule[test-plugin-rule]:plugin_rule effect"));
if (!spawnKitty("plugin_kitty"))
return false;
OK(getFromSocket("/dispatch plugin:test:check_rule"));
NLog::log("{}Reloading config", Colors::YELLOW); NLog::log("{}Reloading config", Colors::YELLOW);
OK(getFromSocket("/reload")); OK(getFromSocket("/reload"));

View file

@ -6,7 +6,6 @@
#include <chrono> #include <chrono>
#include <hyprutils/os/Process.hpp> #include <hyprutils/os/Process.hpp>
#include <hyprutils/memory/WeakPtr.hpp> #include <hyprutils/memory/WeakPtr.hpp>
#include <hyprutils/utils/ScopeGuard.hpp>
#include <csignal> #include <csignal>
#include <cerrno> #include <cerrno>
#include "../shared.hpp" #include "../shared.hpp"
@ -15,7 +14,6 @@ static int ret = 0;
using namespace Hyprutils::OS; using namespace Hyprutils::OS;
using namespace Hyprutils::Memory; using namespace Hyprutils::Memory;
using namespace Hyprutils::Utils;
#define UP CUniquePointer #define UP CUniquePointer
#define SP CSharedPointer #define SP CSharedPointer
@ -27,7 +25,7 @@ static bool test() {
// test on workspace "window" // test on workspace "window"
NLog::log("{}Switching to workspace 1", Colors::YELLOW); NLog::log("{}Switching to workspace 1", Colors::YELLOW);
OK(getFromSocket("/dispatch workspace 1")); getFromSocket("/dispatch workspace 1");
NLog::log("{}Checking persistent no-mon", Colors::YELLOW); NLog::log("{}Checking persistent no-mon", Colors::YELLOW);
OK(getFromSocket("r/keyword workspace 966,persistent:1")); OK(getFromSocket("r/keyword workspace 966,persistent:1"));
@ -361,95 +359,6 @@ static bool test() {
NLog::log("{}Killing all windows", Colors::YELLOW); NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows(); Tests::killAllWindows();
NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW);
{
CScopeGuard guard = {[&]() {
NLog::log("{}Cleaning up asymmetric gap test", Colors::YELLOW);
Tests::killAllWindows();
OK(getFromSocket("/reload"));
}};
OK(getFromSocket("/dispatch workspace name:gap_split_test"));
OK(getFromSocket("r/keyword general:gaps_in 0"));
OK(getFromSocket("r/keyword general:border_size 0"));
OK(getFromSocket("r/keyword dwindle:split_width_multiplier 1.0"));
OK(getFromSocket("r/keyword workspace name:gap_split_test,gapsout:0 1000 0 0"));
NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW);
OK(getFromSocket("r/keyword dwindle:force_split 0"));
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) {
return false;
}
NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
Tests::killAllWindows();
EXPECT(Tests::windowCount(), 0);
NLog::log("{}Testing force_split = 1", Colors::YELLOW);
OK(getFromSocket("r/keyword dwindle:force_split 1"));
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) {
return false;
}
NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
NLog::log("{}Expecting horizontal split (C left of B)", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
if (!Tests::spawnKitty("gaps_kitty_C")) {
return false;
}
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0");
Tests::killAllWindows();
EXPECT(Tests::windowCount(), 0);
NLog::log("{}Testing force_split = 2", Colors::YELLOW);
OK(getFromSocket("r/keyword dwindle:force_split 2"));
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) {
return false;
}
NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
NLog::log("{}Expecting horizontal split (C right of A)", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
if (!Tests::spawnKitty("gaps_kitty_C")) {
return false;
}
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C"));
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0");
}
// kill all
NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows();
NLog::log("{}Expecting 0 windows", Colors::YELLOW); NLog::log("{}Expecting 0 windows", Colors::YELLOW);
EXPECT(Tests::windowCount(), 0); EXPECT(Tests::windowCount(), 0);

View file

@ -318,28 +318,70 @@ submap = reset
### WINDOWS AND WORKSPACES ### ### WINDOWS AND WORKSPACES ###
############################## ##############################
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more windowrule {
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# Example windowrule v1
# windowrule = float, ^(kitty)$
# Example windowrule v2
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
# Ignore maximize requests from apps. You'll probably like this. # Ignore maximize requests from apps. You'll probably like this.
windowrulev2 = suppressevent maximize, class:.* name = suppress-maximize-events
match:class = .*
suppress_event = maximize
}
windowrule {
# Fix some dragging issues with XWayland # Fix some dragging issues with XWayland
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0 name = fix-xwayland-drags
match:class = ^$
match:title = ^$
match:xwayland = true
match:float = true
match:fullscreen = false
match:pin = false
no_focus = true
}
# Workspace "windows" is a smart gaps one
workspace = n[s:window] w[tv1], gapsout:0, gapsin:0 workspace = n[s:window] w[tv1], gapsout:0, gapsin:0
workspace = n[s:window] f[1], gapsout:0, gapsin:0 workspace = n[s:window] f[1], gapsout:0, gapsin:0
windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] w[tv1]
windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] w[tv1] windowrule {
windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] f[1] name = smart-gaps-1
windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] f[1] match:float = false
match:workspace = n[s:window] w[tv1]
border_size = 0
rounding = 0
}
windowrule {
name = smart-gaps-2
match:float = false
match:workspace = n[s:window] f[1]
border_size = 0
rounding = 0
}
windowrule {
name = wr-kitty-stuff
match:class = wr_kitty
float = true
size = 200 200
pin = false
}
windowrule {
name = tagged-kitty-floats
match:tag = tag_kitty
float = true
}
windowrule {
name = static-kitty-tag
match:class = tag_kitty
tag = +tag_kitty
}
gesture = 3, left, dispatcher, exec, kitty gesture = 3, left, dispatcher, exec, kitty
gesture = 3, right, float gesture = 3, right, float
@ -356,7 +398,3 @@ gesture = 5, left, dispatcher, sendshortcut, , i, activewindow
gesture = 5, right, dispatcher, sendshortcut, , t, activewindow gesture = 5, right, dispatcher, sendshortcut, , t, activewindow
gesture = 4, right, dispatcher, sendshortcut, , return, activewindow gesture = 4, right, dispatcher, sendshortcut, , return, activewindow
gesture = 4, left, dispatcher, movecursortocorner, 1 gesture = 4, left, dispatcher, movecursortocorner, 1
windowrule = float, pin, class:wr_kitty
windowrule = size 200 200, class:wr_kitty
windowrule = unset pin, class:wr_kitty

View file

@ -1,151 +0,0 @@
project(
'Hyprland',
'cpp',
'c',
version: run_command('cat', join_paths(meson.project_source_root(), 'VERSION'), check: true).stdout().strip(),
default_options: [
'warning_level=2',
'default_library=static',
'optimization=3',
'buildtype=release',
'debug=false',
'b_lto=false',
'cpp_std=c++26',
],
meson_version: '>= 1.1.0',
)
datarootdir = '-DDATAROOTDIR="' + get_option('prefix') / get_option('datadir') + '"'
add_project_arguments(
[
'-Wno-unused-parameter',
'-Wno-unused-value',
'-Wno-missing-field-initializers',
'-Wno-narrowing',
'-Wno-pointer-arith',
datarootdir,
'-DHYPRLAND_VERSION="' + meson.project_version() + '"',
],
language: 'cpp',
)
cpp_compiler = meson.get_compiler('cpp')
if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif
aquamarine = dependency('aquamarine', version: '>=0.9.3')
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
hyprgraphics = dependency('hyprgraphics', version: '>=0.1.6')
hyprlang = dependency('hyprlang', version: '>=0.3.2')
hyprutils = dependency('hyprutils', version: '>=0.8.2')
aq_ver_list = aquamarine.version().split('.')
git = find_program('git', required: false)
if git.found()
git_hash = run_command(git, 'rev-parse', 'HEAD').stdout().strip()
git_branch = run_command(git, 'branch', '--show-current').stdout().strip()
git_message = run_command(git, 'show', '-s', '--format=%s').stdout().strip()
git_date = run_command(git, 'show', '-s', '--format=%cd', '--date=local').stdout().strip()
git_dirty = run_command(git, 'diff-index', '--quiet', 'HEAD', '--', check: false).returncode() != 0 ? 'dirty' : 'clean'
git_tag = run_command(git, 'describe', '--tags').stdout().strip()
git_commits = run_command(git, 'rev-list', '--count', 'HEAD').stdout().strip()
else
git_hash = 'unknown'
git_branch = 'unknown'
git_message = 'unknown'
git_date = 'unknown'
git_dirty = 'unknown'
git_tag = 'unknown'
git_commits = '0'
endif
cfg = configuration_data()
cfg.set('GIT_COMMIT_HASH', git_hash)
cfg.set('GIT_BRANCH', git_branch)
cfg.set('GIT_COMMIT_MESSAGE', git_message)
cfg.set('GIT_COMMIT_DATE', git_date)
cfg.set('GIT_DIRTY', git_dirty)
cfg.set('GIT_TAG', git_tag)
cfg.set('GIT_COMMITS', git_commits)
cfg.set('AQUAMARINE_VERSION', aquamarine.version())
cfg.set('AQUAMARINE_VERSION_MAJOR', aq_ver_list[0])
cfg.set('AQUAMARINE_VERSION_MINOR', aq_ver_list[1])
cfg.set('AQUAMARINE_VERSION_PATCH', aq_ver_list[2])
cfg.set('HYPRLANG_VERSION', hyprlang.version())
cfg.set('HYPRUTILS_VERSION', hyprutils.version())
cfg.set('HYPRCURSOR_VERSION', hyprcursor.version())
cfg.set('HYPRGRAPHICS_VERSION', hyprgraphics.version())
version_h = configure_file(
input: 'src/version.h.in',
output: 'version.h',
configuration: cfg
)
install_headers(version_h, subdir: 'hyprland/src')
xcb_dep = dependency('xcb', required: get_option('xwayland'))
xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland'))
xcb_errors_dep = dependency('xcb-errors', required: get_option('xwayland'))
xcb_icccm_dep = dependency('xcb-icccm', required: get_option('xwayland'))
xcb_render_dep = dependency('xcb-render', required: get_option('xwayland'))
xcb_res_dep = dependency('xcb-res', required: get_option('xwayland'))
xcb_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland'))
gio_dep = dependency('gio-2.0', required: true)
if not xcb_dep.found()
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
epoll_dep = dependency('epoll-shim', required: false)
inotify_dep = dependency('libinotify', required: false)
re2 = dependency('re2', required: true)
systemd_option = get_option('systemd')
systemd = dependency('systemd', required: systemd_option)
systemd_option.enable_auto_if(systemd.found())
if (systemd_option.enabled())
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
subdir('systemd')
endif
if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif
run_command('sh', '-c', 'scripts/generateShaderIncludes.sh', check: true)
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.inc', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
install_headers(file, subdir: 'hyprland', preserve_path: true)
endforeach
install_headers(version_h, subdir: 'src')
tracy = dependency('tracy', static: true, required: get_option('tracy_enable'))
if get_option('tracy_enable') and get_option('buildtype') != 'debugoptimized'
warning('Profiling builds should set -- buildtype = debugoptimized')
endif
subdir('protocols')
subdir('src')
subdir('hyprctl')
subdir('assets')
subdir('example')
subdir('docs')
if get_option('hyprpm').enabled()
subdir('hyprpm/src')
endif
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')
import('pkgconfig').generate(
name: 'Hyprland',
filebase: 'hyprland',
url: 'https://github.com/hyprwm/Hyprland',
description: 'Hyprland header files',
install_dir: pkg_install_dir,
subdirs: ['', 'hyprland/protocols', 'hyprland'],
)

View file

@ -1,5 +0,0 @@
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
option('uwsm', type: 'feature', value: 'enabled', description: 'Enable uwsm integration (only if systemd is enabled)')
option('hyprpm', type: 'feature', value: 'enabled', description: 'Enable hyprpm')
option('tracy_enable', type: 'boolean', value: false , description: 'Enable profiling')

View file

@ -26,6 +26,7 @@
libxkbcommon, libxkbcommon,
libuuid, libuuid,
libgbm, libgbm,
muparser,
pango, pango,
pciutils, pciutils,
re2, re2,
@ -45,12 +46,12 @@
commit, commit,
revCount, revCount,
date, date,
withHyprtester ? false,
# deprecated flags # deprecated flags
enableNvidiaPatches ? false, enableNvidiaPatches ? false,
nvidiaPatches ? false, nvidiaPatches ? false,
hidpiXWayland ? false, hidpiXWayland ? false,
legacyRenderer ? false, legacyRenderer ? false,
withHyprtester ? false,
}: let }: let
inherit (builtins) foldl' readFile; inherit (builtins) foldl' readFile;
inherit (lib.asserts) assertMsg; inherit (lib.asserts) assertMsg;
@ -70,6 +71,7 @@ in
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed."; assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland"; assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland";
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported."; assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
assert assertMsg (!withHyprtester) "The option `withHyprtester` has been removed. Hyprtester is always built now.";
customStdenv.mkDerivation (finalAttrs: { customStdenv.mkDerivation (finalAttrs: {
pname = "hyprland${optionalString debug "-debug"}"; pname = "hyprland${optionalString debug "-debug"}";
inherit version; inherit version;
@ -85,6 +87,7 @@ in
../assets/install ../assets/install
../hyprctl ../hyprctl
../hyprland.pc.in ../hyprland.pc.in
../hyprtester
../LICENSE ../LICENSE
../protocols ../protocols
../src ../src
@ -94,7 +97,6 @@ in
(fs.fileFilter (file: file.hasExt "conf" || file.hasExt "desktop") ../example) (fs.fileFilter (file: file.hasExt "conf" || file.hasExt "desktop") ../example)
(fs.fileFilter (file: file.hasExt "sh") ../scripts) (fs.fileFilter (file: file.hasExt "sh") ../scripts)
(fs.fileFilter (file: file.name == "CMakeLists.txt") ../.) (fs.fileFilter (file: file.name == "CMakeLists.txt") ../.)
(optional withHyprtester ../hyprtester)
])); ]));
}; };
@ -106,11 +108,13 @@ in
sed -i "s#@PREFIX@/##g" hyprland.pc.in sed -i "s#@PREFIX@/##g" hyprland.pc.in
''; '';
COMMITS = revCount; env = {
DATE = date; GIT_COMMITS = revCount;
DIRTY = optionalString (commit == "") "dirty"; GIT_COMMIT_DATE = date;
HASH = commit; GIT_COMMIT_HASH = commit;
TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}"; GIT_DIRTY = if (commit == "") then "clean" else "dirty";
GIT_TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
};
depsBuildBuild = [ depsBuildBuild = [
pkg-config pkg-config
@ -146,6 +150,7 @@ in
libuuid libuuid
libxkbcommon libxkbcommon
libgbm libgbm
muparser
pango pango
pciutils pciutils
re2 re2
@ -187,7 +192,7 @@ in
"NO_UWSM" = true; "NO_UWSM" = true;
"NO_HYPRPM" = true; "NO_HYPRPM" = true;
"TRACY_ENABLE" = false; "TRACY_ENABLE" = false;
"BUILD_HYPRTESTER" = withHyprtester; "BUILD_HYPRTESTER" = true;
}; };
preConfigure = '' preConfigure = ''
@ -206,7 +211,7 @@ in
pkgconf pkgconf
]} ]}
''} ''}
'' + optionalString withHyprtester ''
install hyprtester/pointer-warp -t $out/bin install hyprtester/pointer-warp -t $out/bin
install hyprtester/pointer-scroll -t $out/bin install hyprtester/pointer-scroll -t $out/bin
''; '';

View file

@ -43,7 +43,12 @@ in {
}; };
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;}; hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
hyprland-with-hyprtester = final.hyprland.override {withHyprtester = true;}; hyprland-with-hyprtester =
builtins.trace ''
hyprland-with-hyprtester was removed. Please use the hyprland package.
Hyprtester is always built now.
''
final.hyprland;
# deprecated packages # deprecated packages
hyprland-legacy-renderer = hyprland-legacy-renderer =

View file

@ -1,6 +1,6 @@
inputs: pkgs: let inputs: pkgs: let
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}; flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
hyprland = flake.hyprland-with-hyprtester; hyprland = flake.hyprland;
in { in {
tests = pkgs.testers.runNixOSTest { tests = pkgs.testers.runNixOSTest {
name = "hyprland-tests"; name = "hyprland-tests";

View file

@ -1,119 +0,0 @@
wayland_protos = dependency(
'wayland-protocols',
version: '>=1.45',
fallback: 'wayland-protocols',
default_options: ['tests=false'],
)
hyprland_protos = dependency(
'hyprland-protocols',
version: '>=0.6.4',
fallback: 'hyprland-protocols',
)
wayland_protocol_dir = wayland_protos.get_variable('pkgdatadir')
hyprland_protocol_dir = hyprland_protos.get_variable('pkgdatadir')
hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.10', native: true)
hyprwayland_scanner = find_program(
hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'),
native: true,
)
protocols = [
'wlr-gamma-control-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
'wlr-output-power-management-unstable-v1.xml',
'input-method-unstable-v2.xml',
'virtual-keyboard-unstable-v1.xml',
'wlr-virtual-pointer-unstable-v1.xml',
'wlr-output-management-unstable-v1.xml',
'kde-server-decoration.xml',
'wlr-layer-shell-unstable-v1.xml',
'wayland-drm.xml',
'wlr-data-control-unstable-v1.xml',
'wlr-screencopy-unstable-v1.xml',
'xx-color-management-v4.xml',
'frog-color-management-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-toplevel-mapping-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-surface-v1.xml',
hyprland_protocol_dir / 'protocols/hyprland-lock-notify-v1.xml',
wayland_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
wayland_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml',
wayland_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
wayland_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
wayland_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml',
wayland_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml',
wayland_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
wayland_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
wayland_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
wayland_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml',
wayland_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml',
wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml',
wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v1.xml',
wayland_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
wayland_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml',
wayland_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml',
wayland_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml',
wayland_protocol_dir / 'stable/tablet/tablet-v2.xml',
wayland_protocol_dir / 'stable/presentation-time/presentation-time.xml',
wayland_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
wayland_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml',
wayland_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
wayland_protocol_dir / 'stable/viewporter/viewporter.xml',
wayland_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml',
wayland_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
wayland_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml',
wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
wayland_protocol_dir / 'staging/content-type/content-type-v1.xml',
wayland_protocol_dir / 'staging/color-management/color-management-v1.xml',
wayland_protocol_dir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml',
wayland_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml',
wayland_protocol_dir / 'staging/ext-workspace/ext-workspace-v1.xml',
wayland_protocol_dir / 'staging/ext-data-control/ext-data-control-v1.xml',
wayland_protocol_dir / 'staging/pointer-warp/pointer-warp-v1.xml',
wayland_protocol_dir / 'staging/fifo/fifo-v1.xml',
wayland_protocol_dir / 'staging/commit-timing/commit-timing-v1.xml',
]
wl_protocols = []
foreach protocol : protocols
wl_protocols += custom_target(
protocol.underscorify(),
input: protocol,
install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '@INPUT@', '@OUTDIR@'],
)
endforeach
# wayland.xml generation
wayland_scanner = dependency('wayland-scanner', native: true)
wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir')
wayland_xml = wayland_scanner_datadir / 'wayland.xml'
wayland_protocol = custom_target(
wayland_xml.underscorify(),
input: wayland_xml,
install: true,
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'],
)
lib_server_protos = static_library(
'server_protos',
wl_protocols + wayland_protocol,
)
server_protos = declare_dependency(
link_with: lib_server_protos,
sources: wl_protocols + wayland_protocol,
)

View file

@ -65,6 +65,7 @@
#include "debug/HyprNotificationOverlay.hpp" #include "debug/HyprNotificationOverlay.hpp"
#include "debug/HyprDebugOverlay.hpp" #include "debug/HyprDebugOverlay.hpp"
#include "helpers/MonitorFrameScheduler.hpp" #include "helpers/MonitorFrameScheduler.hpp"
#include "i18n/Engine.hpp"
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
#include <aquamarine/input/Input.hpp> #include <aquamarine/input/Input.hpp>
@ -900,8 +901,8 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
if (ONLY_PRIORITY && !w->priorityFocus()) if (ONLY_PRIORITY && !w->priorityFocus())
continue; continue;
if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_windowData.noFocus.valueOrDefault() && w != pIgnoreWindow && if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
!isShadowedByModal(w)) { w != pIgnoreWindow && !isShadowedByModal(w)) {
const auto BB = w->getWindowBoxUnified(properties); const auto BB = w->getWindowBoxUnified(properties);
CBox box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0); CBox box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0);
if (box.containsPoint(g_pPointerManager->position())) if (box.containsPoint(g_pPointerManager->position()))
@ -938,7 +939,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
continue; continue;
} }
if (w->m_isFloating && w->m_isMapped && w->m_workspace->isVisible() && !w->isHidden() && !w->m_pinned && !w->m_windowData.noFocus.valueOrDefault() && if (w->m_isFloating && w->m_isMapped && w->m_workspace->isVisible() && !w->isHidden() && !w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
w != pIgnoreWindow && (!aboveFullscreen || w->m_createdOverFullscreen) && !isShadowedByModal(w)) { w != pIgnoreWindow && (!aboveFullscreen || w->m_createdOverFullscreen) && !isShadowedByModal(w)) {
// OR windows should add focus to parent // OR windows should add focus to parent
if (w->m_X11ShouldntFocus && !w->isX11OverrideRedirect()) if (w->m_X11ShouldntFocus && !w->isX11OverrideRedirect())
@ -999,7 +1000,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
continue; continue;
if (!w->m_isX11 && !w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && if (!w->m_isX11 && !w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus &&
!w->m_windowData.noFocus.valueOrDefault() && w != pIgnoreWindow && !isShadowedByModal(w)) { !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pIgnoreWindow && !isShadowedByModal(w)) {
if (w->hasPopupAt(pos)) if (w->hasPopupAt(pos))
return w; return w;
} }
@ -1015,7 +1016,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
if (!w->m_workspace) if (!w->m_workspace)
continue; continue;
if (!w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && !w->m_windowData.noFocus.valueOrDefault() && if (!w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
w != pIgnoreWindow && !isShadowedByModal(w)) { w != pIgnoreWindow && !isShadowedByModal(w)) {
CBox box = (properties & USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_position, w->m_size}; CBox box = (properties & USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_position, w->m_size};
if (box.containsPoint(pos)) if (box.containsPoint(pos))
@ -1151,7 +1152,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
m_lastWindow.reset(); m_lastWindow.reset();
if (PLASTWINDOW && PLASTWINDOW->m_isMapped) { if (PLASTWINDOW && PLASTWINDOW->m_isMapped) {
updateWindowAnimatedDecorationValues(PLASTWINDOW); PLASTWINDOW->updateDecorationValues();
g_pXWaylandManager->activateWindow(PLASTWINDOW, false); g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
} }
@ -1171,7 +1172,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
return; return;
} }
if (pWindow->m_windowData.noFocus.valueOrDefault()) { if (pWindow->m_ruleApplicator->noFocus().valueOrDefault()) {
Debug::log(LOG, "Ignoring focus to nofocus window!"); Debug::log(LOG, "Ignoring focus to nofocus window!");
return; return;
} }
@ -1208,9 +1209,9 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
// we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window // we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window
if (PLASTWINDOW && PLASTWINDOW->m_isMapped) { if (PLASTWINDOW && PLASTWINDOW->m_isMapped) {
PLASTWINDOW->updateDynamicRules(); PLASTWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FOCUS);
updateWindowAnimatedDecorationValues(PLASTWINDOW); PLASTWINDOW->updateDecorationValues();
if (!pWindow->m_isX11 || !pWindow->isX11OverrideRedirect()) if (!pWindow->m_isX11 || !pWindow->isX11OverrideRedirect())
g_pXWaylandManager->activateWindow(PLASTWINDOW, false); g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
@ -1224,10 +1225,10 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface
g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow
pWindow->updateDynamicRules(); pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FOCUS);
pWindow->onFocusAnimUpdate(); pWindow->onFocusAnimUpdate();
updateWindowAnimatedDecorationValues(pWindow); pWindow->updateDecorationValues();
if (pWindow->m_isUrgent) if (pWindow->m_isUrgent)
pWindow->m_isUrgent = false; pWindow->m_isUrgent = false;
@ -1333,7 +1334,7 @@ SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, st
for (auto const& ls : *layerSurfaces | std::views::reverse) { for (auto const& ls : *layerSurfaces | std::views::reverse) {
if (!ls->m_mapped || ls->m_fadingOut || !ls->m_layerSurface || (ls->m_layerSurface && !ls->m_layerSurface->m_surface->m_mapped) || ls->m_alpha->value() == 0.f || if (!ls->m_mapped || ls->m_fadingOut || !ls->m_layerSurface || (ls->m_layerSurface && !ls->m_layerSurface->m_surface->m_mapped) || ls->m_alpha->value() == 0.f ||
(aboveLockscreen && (!ls->m_aboveLockscreen || !ls->m_aboveLockscreenInteractable))) (aboveLockscreen && ls->m_ruleApplicator->aboveLock().valueOrDefault() != 2))
continue; continue;
auto [surf, local] = ls->m_layerSurface->m_surface->at(pos - ls->m_geometry.pos(), true); auto [surf, local] = ls->m_layerSurface->m_surface->at(pos - ls->m_geometry.pos(), true);
@ -1714,7 +1715,7 @@ static bool isFloatingMatches(WINDOWPTR w, std::optional<bool> floating) {
template <typename WINDOWPTR> template <typename WINDOWPTR>
static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace = false) { static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace = false) {
return isFloatingMatches(w, floating) && return isFloatingMatches(w, floating) &&
(w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_isMapped && !w->isHidden() && (!focusableOnly || !w->m_windowData.noFocus.valueOrDefault())); (w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_isMapped && !w->isHidden() && (!focusableOnly || !w->m_ruleApplicator->noFocus().valueOrDefault()));
} }
template <typename Iterator> template <typename Iterator>
@ -1905,103 +1906,10 @@ void CCompositor::updateAllWindowsAnimatedDecorationValues() {
if (!w->m_isMapped) if (!w->m_isMapped)
continue; continue;
updateWindowAnimatedDecorationValues(w); w->updateDecorationValues();
} }
} }
void CCompositor::updateWindowAnimatedDecorationValues(PHLWINDOW pWindow) {
// optimization
static auto PACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.active_border");
static auto PINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.inactive_border");
static auto PNOGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border_active");
static auto PNOGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border");
static auto PGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_active");
static auto PGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_inactive");
static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
static auto PINACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:inactive_opacity");
static auto PACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:active_opacity");
static auto PFULLSCREENALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:fullscreen_opacity");
static auto PSHADOWCOL = CConfigValue<Hyprlang::INT>("decoration:shadow:color");
static auto PSHADOWCOLINACTIVE = CConfigValue<Hyprlang::INT>("decoration:shadow:color_inactive");
static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
static auto PDIMENABLED = CConfigValue<Hyprlang::INT>("decoration:dim_inactive");
static auto PDIMMODAL = CConfigValue<Hyprlang::INT>("decoration:dim_modal");
auto* const ACTIVECOL = sc<CGradientValueData*>((PACTIVECOL.ptr())->getData());
auto* const INACTIVECOL = sc<CGradientValueData*>((PINACTIVECOL.ptr())->getData());
auto* const NOGROUPACTIVECOL = sc<CGradientValueData*>((PNOGROUPACTIVECOL.ptr())->getData());
auto* const NOGROUPINACTIVECOL = sc<CGradientValueData*>((PNOGROUPINACTIVECOL.ptr())->getData());
auto* const GROUPACTIVECOL = sc<CGradientValueData*>((PGROUPACTIVECOL.ptr())->getData());
auto* const GROUPINACTIVECOL = sc<CGradientValueData*>((PGROUPINACTIVECOL.ptr())->getData());
auto* const GROUPACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPACTIVELOCKEDCOL.ptr())->getData());
auto* const GROUPINACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPINACTIVELOCKEDCOL.ptr())->getData());
auto setBorderColor = [&](CGradientValueData grad) -> void {
if (grad == pWindow->m_realBorderColor)
return;
pWindow->m_realBorderColorPrevious = pWindow->m_realBorderColor;
pWindow->m_realBorderColor = grad;
pWindow->m_borderFadeAnimationProgress->setValueAndWarp(0.f);
*pWindow->m_borderFadeAnimationProgress = 1.f;
};
const bool IS_SHADOWED_BY_MODAL = pWindow->m_xdgSurface && pWindow->m_xdgSurface->m_toplevel && pWindow->m_xdgSurface->m_toplevel->anyChildModal();
// border
const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(pWindow);
if (RENDERDATA.isBorderGradient)
setBorderColor(*RENDERDATA.borderGradient);
else {
const bool GROUPLOCKED = pWindow->m_groupData.pNextWindow.lock() ? pWindow->getGroupHead()->m_groupData.locked : false;
if (pWindow == m_lastWindow) {
const auto* const ACTIVECOLOR =
!pWindow->m_groupData.pNextWindow.lock() ? (!pWindow->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
setBorderColor(pWindow->m_windowData.activeBorderColor.valueOr(*ACTIVECOLOR));
} else {
const auto* const INACTIVECOLOR = !pWindow->m_groupData.pNextWindow.lock() ? (!pWindow->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) :
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
setBorderColor(pWindow->m_windowData.inactiveBorderColor.valueOr(*INACTIVECOLOR));
}
}
// opacity
const auto PWORKSPACE = pWindow->m_workspace;
if (pWindow->isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) {
*pWindow->m_activeInactiveAlpha = pWindow->m_windowData.alphaFullscreen.valueOrDefault().applyAlpha(*PFULLSCREENALPHA);
} else {
if (pWindow == m_lastWindow)
*pWindow->m_activeInactiveAlpha = pWindow->m_windowData.alpha.valueOrDefault().applyAlpha(*PACTIVEALPHA);
else
*pWindow->m_activeInactiveAlpha = pWindow->m_windowData.alphaInactive.valueOrDefault().applyAlpha(*PINACTIVEALPHA);
}
// dim
float goalDim = 1.F;
if (pWindow == m_lastWindow.lock() || pWindow->m_windowData.noDim.valueOrDefault() || !*PDIMENABLED)
goalDim = 0;
else
goalDim = *PDIMSTRENGTH;
if (IS_SHADOWED_BY_MODAL && *PDIMMODAL)
goalDim += (1.F - goalDim) / 2.F;
*pWindow->m_dimPercent = goalDim;
// shadow
if (!pWindow->isX11OverrideRedirect() && !pWindow->m_X11DoesntWantBorders) {
if (pWindow == m_lastWindow)
*pWindow->m_realShadowColor = CHyprColor(*PSHADOWCOL);
else
*pWindow->m_realShadowColor = CHyprColor(*PSHADOWCOLINACTIVE != -1 ? *PSHADOWCOLINACTIVE : *PSHADOWCOL);
} else {
pWindow->m_realShadowColor->setValueAndWarp(CHyprColor(0, 0, 0, 0)); // no shadow
}
pWindow->updateWindowDecos();
}
MONITORID CCompositor::getNextAvailableMonitorID(std::string const& name) { MONITORID CCompositor::getNextAvailableMonitorID(std::string const& name) {
// reuse ID if it's already in the map, and the monitor with that ID is not being used by another monitor // reuse ID if it's already in the map, and the monitor with that ID is not being used by another monitor
if (m_monitorIDMap.contains(name) && !std::ranges::any_of(m_realMonitors, [&](auto m) { return m->m_id == m_monitorIDMap[name]; })) if (m_monitorIDMap.contains(name) && !std::ranges::any_of(m_realMonitors, [&](auto m) { return m->m_id == m_monitorIDMap[name]; }))
@ -2340,14 +2248,14 @@ void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, cons
} }
void CCompositor::setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) { void CCompositor::setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
if (PWINDOW->m_windowData.syncFullscreen.valueOrDefault()) if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE}); setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE});
else else
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = PWINDOW->m_fullscreenState.client}); setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = PWINDOW->m_fullscreenState.client});
} }
void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) { void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
if (PWINDOW->m_windowData.syncFullscreen.valueOrDefault()) if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE}); setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = MODE});
else else
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = PWINDOW->m_fullscreenState.internal, .client = MODE}); setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = PWINDOW->m_fullscreenState.internal, .client = MODE});
@ -2388,15 +2296,16 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
} }
// TODO: update the state on syncFullscreen changes // TODO: update the state on syncFullscreen changes
if (!CHANGEINTERNAL && PWINDOW->m_windowData.syncFullscreen.valueOrDefault()) if (!CHANGEINTERNAL && PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
return; return;
PWINDOW->m_fullscreenState.client = state.client; PWINDOW->m_fullscreenState.client = state.client;
g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN); g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN);
if (!CHANGEINTERNAL) { if (!CHANGEINTERNAL) {
PWINDOW->updateDynamicRules(); PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
updateWindowAnimatedDecorationValues(PWINDOW); Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
PWINDOW->updateDecorationValues();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID()); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
return; return;
} }
@ -2410,8 +2319,10 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc<int>(EFFECTIVE_MODE) != FSMODE_NONE)}); g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc<int>(EFFECTIVE_MODE) != FSMODE_NONE)});
EMIT_HOOK_EVENT("fullscreen", PWINDOW); EMIT_HOOK_EVENT("fullscreen", PWINDOW);
PWINDOW->updateDynamicRules(); PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
updateWindowAnimatedDecorationValues(PWINDOW); Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
PWINDOW->updateDecorationValues();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID()); g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
// make all windows on the same workspace under the fullscreen window // make all windows on the same workspace under the fullscreen window
@ -2551,7 +2462,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp_) {
} }
case MODE_TAG_REGEX: { case MODE_TAG_REGEX: {
bool tagMatched = false; bool tagMatched = false;
for (auto const& t : w->m_tags.getTags()) { for (auto const& t : w->m_ruleApplicator->m_tagKeeper.getTags()) {
if (RE2::FullMatch(t, regexCheck)) { if (RE2::FullMatch(t, regexCheck)) {
tagMatched = true; tagMatched = true;
break; break;
@ -2765,22 +2676,19 @@ void CCompositor::performUserChecks() {
const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP"); const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP");
if (!CURRENT_DESKTOP_ENV || std::string{CURRENT_DESKTOP_ENV} != "Hyprland") { if (!CURRENT_DESKTOP_ENV || std::string{CURRENT_DESKTOP_ENV} != "Hyprland") {
g_pHyprNotificationOverlay->addNotification( g_pHyprNotificationOverlay->addNotification(
std::format("Your XDG_CURRENT_DESKTOP environment seems to be managed externally, and the current value is {}.\nThis might cause issues unless it's intentional.", I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, {{"value", CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"}}), CHyprColor{}, 15000,
CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"), ICON_WARNING);
CHyprColor{}, 15000, ICON_WARNING);
} }
} }
if (!*PNOCHECKGUIUTILS) { if (!*PNOCHECKGUIUTILS) {
if (!NFsUtils::executableExistsInPath("hyprland-dialog")) { if (!NFsUtils::executableExistsInPath("hyprland-dialog")) {
g_pHyprNotificationOverlay->addNotification( g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_GUIUTILS), CHyprColor{}, 15000, ICON_WARNING);
"Your system does not have hyprland-guiutils installed. This is a runtime dependency for some dialogs. Consider installing it.", CHyprColor{}, 15000, ICON_WARNING);
} }
} }
if (g_pHyprOpenGL->m_failedAssetsNo > 0) { if (g_pHyprOpenGL->m_failedAssetsNo > 0) {
g_pHyprNotificationOverlay->addNotification(std::format("Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging!", g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_ASSETS, {{"count", std::to_string(g_pHyprOpenGL->m_failedAssetsNo)}}),
g_pHyprOpenGL->m_failedAssetsNo, g_pHyprOpenGL->m_failedAssetsNo > 1 ? "s" : ""),
CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR); CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR);
} }
} }
@ -2845,7 +2753,7 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
} }
pWindow->updateToplevel(); pWindow->updateToplevel();
pWindow->updateDynamicRules(); pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
pWindow->uncacheWindowDecos(); pWindow->uncacheWindowDecos();
pWindow->updateGroupOutputs(); pWindow->updateGroupOutputs();
@ -2876,7 +2784,7 @@ PHLWINDOW CCompositor::getForceFocus() {
if (!w->m_isMapped || w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible()) if (!w->m_isMapped || w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible())
continue; continue;
if (!w->m_stayFocused) if (!w->m_ruleApplicator->stayFocused().valueOrDefault())
continue; continue;
return w; return w;
@ -2905,10 +2813,8 @@ void CCompositor::checkMonitorOverlaps() {
for (const auto& m : m_monitors) { for (const auto& m : m_monitors) {
if (!monitorRegion.copy().intersect(m->logicalBox()).empty()) { if (!monitorRegion.copy().intersect(m->logicalBox()).empty()) {
Debug::log(ERR, "Monitor {}: detected overlap with layout", m->m_name); Debug::log(ERR, "Monitor {}: detected overlap with layout", m->m_name);
g_pHyprNotificationOverlay->addNotification(std::format("Your monitor layout is set up incorrectly. Monitor {} overlaps with other monitor(s) in the " g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, {{"name", m->m_name}}), CHyprColor{}, 15000,
"layout.\nPlease see the wiki (Monitors page) for more. This will cause issues.", ICON_WARNING);
m->m_name),
CHyprColor{}, 15000, ICON_WARNING);
break; break;
} }
@ -3029,6 +2935,8 @@ void CCompositor::arrangeMonitors() {
#ifndef NO_XWAYLAND #ifndef NO_XWAYLAND
CBox box = g_pCompositor->calculateX11WorkArea(); CBox box = g_pCompositor->calculateX11WorkArea();
if (!g_pXWayland || !g_pXWayland->m_wm)
return;
g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h); g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h);
#endif #endif
} }

View file

@ -129,7 +129,6 @@ class CCompositor {
PHLMONITOR getMonitorInDirection(const char&); PHLMONITOR getMonitorInDirection(const char&);
PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&); PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&);
void updateAllWindowsAnimatedDecorationValues(); void updateAllWindowsAnimatedDecorationValues();
void updateWindowAnimatedDecorationValues(PHLWINDOW);
MONITORID getNextAvailableMonitorID(std::string const& name); MONITORID getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false); void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR); void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR);

View file

@ -15,12 +15,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT, .type = CONFIG_OPTION_INT,
.data = SConfigOptionDescription::SRangeData{1, 0, 20}, .data = SConfigOptionDescription::SRangeData{1, 0, 20},
}, },
SConfigOptionDescription{
.value = "general:no_border_on_floating",
.description = "disable borders for floating windows",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{ SConfigOptionDescription{
.value = "general:gaps_in", .value = "general:gaps_in",
.description = "gaps between windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20)", .description = "gaps between windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20)",
@ -1115,6 +1109,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL, .type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SRangeData{0, -20, 20}, .data = SConfigOptionDescription::SRangeData{0, -20, 20},
}, },
SConfigOptionDescription{
.value = "group:groupbar:blur",
.description = "enable background blur for groupbars",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
/* /*
* misc: * misc:

View file

@ -8,12 +8,15 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "config/ConfigDataValues.hpp" #include "config/ConfigDataValues.hpp"
#include "config/ConfigValue.hpp" #include "config/ConfigValue.hpp"
#include "../desktop/WindowRule.hpp"
#include "../protocols/LayerShell.hpp" #include "../protocols/LayerShell.hpp"
#include "../xwayland/XWayland.hpp" #include "../xwayland/XWayland.hpp"
#include "../protocols/OutputManagement.hpp" #include "../protocols/OutputManagement.hpp"
#include "../managers/animation/AnimationManager.hpp" #include "../managers/animation/AnimationManager.hpp"
#include "../desktop/LayerSurface.hpp" #include "../desktop/LayerSurface.hpp"
#include "../desktop/rule/Engine.hpp"
#include "../desktop/rule/windowRule/WindowRule.hpp"
#include "../desktop/rule/layerRule/LayerRule.hpp"
#include "../debug/HyprCtl.hpp"
#include "defaultConfig.hpp" #include "defaultConfig.hpp"
#include "../render/Renderer.hpp" #include "../render/Renderer.hpp"
@ -299,54 +302,6 @@ static Hyprlang::CParseResult handleUnbind(const char* c, const char* v) {
return result; return result;
} }
static Hyprlang::CParseResult handleWindowRule(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
static Hyprlang::CParseResult handleLayerRule(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleLayerRule(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
static Hyprlang::CParseResult handleWindowRuleV2(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleWindowRule(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
static Hyprlang::CParseResult handleBlurLS(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleBlurLS(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) { static Hyprlang::CParseResult handleWorkspaceRules(const char* c, const char* v) {
const std::string VALUE = v; const std::string VALUE = v;
const std::string COMMAND = c; const std::string COMMAND = c;
@ -431,6 +386,30 @@ static Hyprlang::CParseResult handleGesture(const char* c, const char* v) {
return result; return result;
} }
static Hyprlang::CParseResult handleWindowrule(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleWindowrule(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
static Hyprlang::CParseResult handleLayerrule(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
const auto RESULT = g_pConfigManager->handleLayerrule(COMMAND, VALUE);
Hyprlang::CParseResult result;
if (RESULT.has_value())
result.setError(RESULT.value().c_str());
return result;
}
void CConfigManager::registerConfigVar(const char* name, const Hyprlang::INT& val) { void CConfigManager::registerConfigVar(const char* name, const Hyprlang::INT& val) {
m_configValueNumber++; m_configValueNumber++;
m_config->addConfigValue(name, val); m_config->addConfigValue(name, val);
@ -463,7 +442,6 @@ CConfigManager::CConfigManager() {
m_config = makeUnique<Hyprlang::CConfig>(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true}); m_config = makeUnique<Hyprlang::CConfig>(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true});
registerConfigVar("general:border_size", Hyprlang::INT{1}); registerConfigVar("general:border_size", Hyprlang::INT{1});
registerConfigVar("general:no_border_on_floating", Hyprlang::INT{0});
registerConfigVar("general:gaps_in", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "5"}); registerConfigVar("general:gaps_in", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "5"});
registerConfigVar("general:gaps_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"}); registerConfigVar("general:gaps_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"});
registerConfigVar("general:float_gaps", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "0"}); registerConfigVar("general:float_gaps", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "0"});
@ -560,6 +538,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("group:groupbar:gaps_in", Hyprlang::INT{2}); registerConfigVar("group:groupbar:gaps_in", Hyprlang::INT{2});
registerConfigVar("group:groupbar:keep_upper_gap", Hyprlang::INT{1}); registerConfigVar("group:groupbar:keep_upper_gap", Hyprlang::INT{1});
registerConfigVar("group:groupbar:text_offset", Hyprlang::INT{0}); registerConfigVar("group:groupbar:text_offset", Hyprlang::INT{0});
registerConfigVar("group:groupbar:blur", Hyprlang::INT{0});
registerConfigVar("debug:log_damage", Hyprlang::INT{0}); registerConfigVar("debug:log_damage", Hyprlang::INT{0});
registerConfigVar("debug:overlay", Hyprlang::INT{0}); registerConfigVar("debug:overlay", Hyprlang::INT{0});
@ -857,6 +836,16 @@ CConfigManager::CConfigManager() {
m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1}); m_config->addSpecialConfigValue("monitorv2", "max_luminance", Hyprlang::INT{-1});
m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1}); m_config->addSpecialConfigValue("monitorv2", "max_avg_luminance", Hyprlang::INT{-1});
// windowrule v3
m_config->addSpecialCategory("windowrule", {.key = "name"});
m_config->addSpecialConfigValue("windowrule", "enable", Hyprlang::INT{1});
// layerrule v2
m_config->addSpecialCategory("layerrule", {.key = "name"});
m_config->addSpecialConfigValue("layerrule", "enable", Hyprlang::INT{1});
reloadRuleConfigs();
// keywords // keywords
m_config->registerHandler(&::handleExec, "exec", {false}); m_config->registerHandler(&::handleExec, "exec", {false});
m_config->registerHandler(&::handleRawExec, "execr", {false}); m_config->registerHandler(&::handleRawExec, "execr", {false});
@ -867,14 +856,12 @@ CConfigManager::CConfigManager() {
m_config->registerHandler(&::handleBind, "bind", {true}); m_config->registerHandler(&::handleBind, "bind", {true});
m_config->registerHandler(&::handleUnbind, "unbind", {false}); m_config->registerHandler(&::handleUnbind, "unbind", {false});
m_config->registerHandler(&::handleWorkspaceRules, "workspace", {false}); m_config->registerHandler(&::handleWorkspaceRules, "workspace", {false});
m_config->registerHandler(&::handleWindowRule, "windowrule", {false}); m_config->registerHandler(&::handleWindowrule, "windowrule", {false});
m_config->registerHandler(&::handleLayerRule, "layerrule", {false}); m_config->registerHandler(&::handleLayerrule, "layerrule", {false});
m_config->registerHandler(&::handleWindowRuleV2, "windowrulev2", {false});
m_config->registerHandler(&::handleBezier, "bezier", {false}); m_config->registerHandler(&::handleBezier, "bezier", {false});
m_config->registerHandler(&::handleAnimation, "animation", {false}); m_config->registerHandler(&::handleAnimation, "animation", {false});
m_config->registerHandler(&::handleSource, "source", {false}); m_config->registerHandler(&::handleSource, "source", {false});
m_config->registerHandler(&::handleSubmap, "submap", {false}); m_config->registerHandler(&::handleSubmap, "submap", {false});
m_config->registerHandler(&::handleBlurLS, "blurls", {false});
m_config->registerHandler(&::handlePlugin, "plugin", {false}); m_config->registerHandler(&::handlePlugin, "plugin", {false});
m_config->registerHandler(&::handlePermission, "permission", {false}); m_config->registerHandler(&::handlePermission, "permission", {false});
m_config->registerHandler(&::handleGesture, "gesture", {false}); m_config->registerHandler(&::handleGesture, "gesture", {false});
@ -904,6 +891,26 @@ CConfigManager::CConfigManager() {
g_pEventLoopManager->doLater([ERR] { g_pHyprError->queueCreate(ERR.value(), CHyprColor{1.0, 0.1, 0.1, 1.0}); }); g_pEventLoopManager->doLater([ERR] { g_pHyprError->queueCreate(ERR.value(), CHyprColor{1.0, 0.1, 0.1, 1.0}); });
} }
void CConfigManager::reloadRuleConfigs() {
// FIXME: this should also remove old values if they are removed
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
m_config->addSpecialConfigValue("windowrule", ("match:" + r).c_str(), Hyprlang::STRING{""});
}
for (const auto& r : Desktop::Rule::windowEffects()->allEffectStrings()) {
m_config->addSpecialConfigValue("windowrule", r.c_str(), Hyprlang::STRING{""});
}
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
m_config->addSpecialConfigValue("layerrule", ("match:" + r).c_str(), Hyprlang::STRING{""});
}
for (const auto& r : Desktop::Rule::layerEffects()->allEffectStrings()) {
m_config->addSpecialConfigValue("layerrule", r.c_str(), Hyprlang::STRING{""});
}
}
std::optional<std::string> CConfigManager::generateConfig(std::string configPath) { std::optional<std::string> CConfigManager::generateConfig(std::string configPath) {
std::string parentPath = std::filesystem::path(configPath).parent_path(); std::string parentPath = std::filesystem::path(configPath).parent_path();
@ -983,13 +990,36 @@ std::string CConfigManager::getErrors() {
return m_configErrors; return m_configErrors;
} }
static std::vector<const char*> HL_VERSION_VARS = {
"HYPRLAND_V_0_53",
};
static void exportHlVersionVars() {
for (const auto& v : HL_VERSION_VARS) {
setenv(v, "1", 1);
}
}
static void clearHlVersionVars() {
for (const auto& v : HL_VERSION_VARS) {
unsetenv(v);
}
}
void CConfigManager::reload() { void CConfigManager::reload() {
EMIT_HOOK_EVENT("preConfigReload", nullptr); EMIT_HOOK_EVENT("preConfigReload", nullptr);
setDefaultAnimationVars(); setDefaultAnimationVars();
resetHLConfig(); resetHLConfig();
m_configCurrentPath = getMainConfigPath(); m_configCurrentPath = getMainConfigPath();
exportHlVersionVars();
const auto ERR = m_config->parse(); const auto ERR = m_config->parse();
clearHlVersionVars();
const auto monitorError = handleMonitorv2(); const auto monitorError = handleMonitorv2();
const auto ruleError = reloadRules();
m_lastConfigVerificationWasSuccessful = !ERR.error && !monitorError.error; m_lastConfigVerificationWasSuccessful = !ERR.error && !monitorError.error;
postConfigReload(ERR.error || !monitorError.error ? ERR : monitorError); postConfigReload(ERR.error || !monitorError.error ? ERR : monitorError);
} }
@ -1057,20 +1087,18 @@ void CConfigManager::setDefaultAnimationVars() {
std::optional<std::string> CConfigManager::resetHLConfig() { std::optional<std::string> CConfigManager::resetHLConfig() {
m_monitorRules.clear(); m_monitorRules.clear();
m_windowRules.clear();
g_pKeybindManager->clearKeybinds(); g_pKeybindManager->clearKeybinds();
g_pAnimationManager->removeAllBeziers(); g_pAnimationManager->removeAllBeziers();
g_pAnimationManager->addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0)); g_pAnimationManager->addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0));
g_pTrackpadGestures->clearGestures(); g_pTrackpadGestures->clearGestures();
m_mAdditionalReservedAreas.clear(); m_mAdditionalReservedAreas.clear();
m_blurLSNamespaces.clear();
m_workspaceRules.clear(); m_workspaceRules.clear();
setDefaultAnimationVars(); // reset anims setDefaultAnimationVars(); // reset anims
m_declaredPlugins.clear(); m_declaredPlugins.clear();
m_layerRules.clear();
m_failedPluginConfigValues.clear(); m_failedPluginConfigValues.clear();
m_finalExecRequests.clear(); m_finalExecRequests.clear();
m_keywordRules.clear();
// paths // paths
m_configPaths.clear(); m_configPaths.clear();
@ -1080,6 +1108,8 @@ std::optional<std::string> CConfigManager::resetHLConfig() {
const auto RET = verifyConfigExists(); const auto RET = verifyConfigExists();
reloadRuleConfigs();
return RET; return RET;
} }
@ -1178,6 +1208,77 @@ Hyprlang::CParseResult CConfigManager::handleMonitorv2() {
return result; return result;
} }
std::optional<std::string> CConfigManager::addRuleFromConfigKey(const std::string& name) {
const auto ENABLED = m_config->getSpecialConfigValuePtr("windowrule", "enable", name.c_str());
if (ENABLED && ENABLED->m_bSetByUser && std::any_cast<Hyprlang::INT>(ENABLED->getValue()) == 0)
return std::nullopt;
SP<Desktop::Rule::CWindowRule> rule = makeShared<Desktop::Rule::CWindowRule>(name);
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
auto VAL = m_config->getSpecialConfigValuePtr("windowrule", ("match:" + r).c_str(), name.c_str());
if (VAL && VAL->m_bSetByUser)
rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
}
for (const auto& e : Desktop::Rule::windowEffects()->allEffectStrings()) {
auto VAL = m_config->getSpecialConfigValuePtr("windowrule", e.c_str(), name.c_str());
if (VAL && VAL->m_bSetByUser)
rule->addEffect(Desktop::Rule::windowEffects()->get(e).value_or(Desktop::Rule::WINDOW_RULE_EFFECT_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
}
Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
return std::nullopt;
}
std::optional<std::string> CConfigManager::addLayerRuleFromConfigKey(const std::string& name) {
const auto ENABLED = m_config->getSpecialConfigValuePtr("layerrule", "enable", name.c_str());
if (ENABLED && ENABLED->m_bSetByUser && std::any_cast<Hyprlang::INT>(ENABLED->getValue()) != 0)
return std::nullopt;
SP<Desktop::Rule::CLayerRule> rule = makeShared<Desktop::Rule::CLayerRule>(name);
for (const auto& r : Desktop::Rule::allMatchPropStrings()) {
auto VAL = m_config->getSpecialConfigValuePtr("layerrule", ("match:" + r).c_str(), name.c_str());
if (VAL && VAL->m_bSetByUser)
rule->registerMatch(Desktop::Rule::matchPropFromString(r).value_or(Desktop::Rule::RULE_PROP_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
}
for (const auto& e : Desktop::Rule::layerEffects()->allEffectStrings()) {
auto VAL = m_config->getSpecialConfigValuePtr("layerrule", e.c_str(), name.c_str());
if (VAL && VAL->m_bSetByUser)
rule->addEffect(Desktop::Rule::layerEffects()->get(e).value_or(Desktop::Rule::LAYER_RULE_EFFECT_NONE), std::any_cast<Hyprlang::STRING>(VAL->getValue()));
}
Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
return std::nullopt;
}
Hyprlang::CParseResult CConfigManager::reloadRules() {
Desktop::Rule::ruleEngine()->clearAllRules();
Hyprlang::CParseResult result;
for (const auto& name : m_config->listKeysForSpecialCategory("windowrule")) {
const auto error = addRuleFromConfigKey(name);
if (error.has_value())
result.setError(error.value().c_str());
}
for (const auto& name : m_config->listKeysForSpecialCategory("layerrule")) {
const auto error = addLayerRuleFromConfigKey(name);
if (error.has_value())
result.setError(error.value().c_str());
}
for (auto& rule : m_keywordRules) {
Desktop::Rule::ruleEngine()->registerRule(SP<Desktop::Rule::IRule>{rule});
}
Desktop::Rule::ruleEngine()->updateAllRules();
return result;
}
void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) { void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
updateWatcher(); updateWatcher();
@ -1503,229 +1604,6 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1,
return mergedRule; return mergedRule;
} }
std::vector<SP<CWindowRule>> CConfigManager::getMatchingRules(PHLWINDOW pWindow, bool dynamic, bool shadowExec) {
if (!valid(pWindow))
return std::vector<SP<CWindowRule>>();
// if the window is unmapped, don't process exec rules yet.
shadowExec = shadowExec || !pWindow->m_isMapped;
std::vector<SP<CWindowRule>> returns;
Debug::log(LOG, "Searching for matching rules for {} (title: {})", pWindow->m_class, pWindow->m_title);
// since some rules will be applied later, we need to store some flags
bool hasFloating = pWindow->m_isFloating;
bool hasFullscreen = pWindow->isFullscreen();
bool isGrouped = pWindow->m_groupData.pNextWindow;
// local tags for dynamic tag rule match
auto tags = pWindow->m_tags;
for (auto const& rule : m_windowRules) {
// check if we have a matching rule
if (!rule->m_v2) {
try {
if (rule->m_value.starts_with("tag:") && !tags.isTagged(rule->m_value.substr(4)))
continue;
if (rule->m_value.starts_with("title:") && !rule->m_v1Regex.passes(pWindow->m_title))
continue;
if (!rule->m_v1Regex.passes(pWindow->m_class))
continue;
} catch (...) {
Debug::log(ERR, "Regex error at {}", rule->m_value);
continue;
}
} else {
try {
if (rule->m_X11 != -1) {
if (pWindow->m_isX11 != rule->m_X11)
continue;
}
if (rule->m_floating != -1) {
if (hasFloating != rule->m_floating)
continue;
}
if (rule->m_fullscreen != -1) {
if (hasFullscreen != rule->m_fullscreen)
continue;
}
if (rule->m_pinned != -1) {
if (pWindow->m_pinned != rule->m_pinned)
continue;
}
if (rule->m_focus != -1) {
if (rule->m_focus != (g_pCompositor->m_lastWindow.lock() == pWindow))
continue;
}
if (rule->m_group != -1) {
if (rule->m_group != isGrouped)
continue;
}
if (rule->m_modal != -1) {
if (rule->m_modal != pWindow->isModal())
continue;
}
if (!rule->m_fullscreenState.empty()) {
const auto ARGS = CVarList(rule->m_fullscreenState, 2, ' ');
//
std::optional<eFullscreenMode> internalMode, clientMode;
if (ARGS[0] == "*")
internalMode = std::nullopt;
else if (isNumber(ARGS[0]))
internalMode = sc<eFullscreenMode>(std::stoi(ARGS[0]));
else
throw std::runtime_error("szFullscreenState internal mode not valid");
if (ARGS[1] == "*")
clientMode = std::nullopt;
else if (isNumber(ARGS[1]))
clientMode = sc<eFullscreenMode>(std::stoi(ARGS[1]));
else
throw std::runtime_error("szFullscreenState client mode not valid");
if (internalMode.has_value() && pWindow->m_fullscreenState.internal != internalMode)
continue;
if (clientMode.has_value() && pWindow->m_fullscreenState.client != clientMode)
continue;
}
if (!rule->m_onWorkspace.empty()) {
const auto PWORKSPACE = pWindow->m_workspace;
if (!PWORKSPACE || !PWORKSPACE->matchesStaticSelector(rule->m_onWorkspace))
continue;
}
if (!rule->m_contentType.empty()) {
try {
const auto contentType = NContentType::fromString(rule->m_contentType);
if (pWindow->getContentType() != contentType)
continue;
} catch (std::exception& e) { Debug::log(ERR, "Rule \"content:{}\" failed with: {}", rule->m_contentType, e.what()); }
}
if (!rule->m_xdgTag.empty()) {
if (pWindow->xdgTag().value_or("") != rule->m_xdgTag)
continue;
}
if (!rule->m_workspace.empty()) {
const auto PWORKSPACE = pWindow->m_workspace;
if (!PWORKSPACE)
continue;
if (rule->m_workspace.starts_with("name:")) {
if (PWORKSPACE->m_name != rule->m_workspace.substr(5))
continue;
} else {
// number
if (!isNumber(rule->m_workspace))
throw std::runtime_error("szWorkspace not name: or number");
const int64_t ID = std::stoll(rule->m_workspace);
if (PWORKSPACE->m_id != ID)
continue;
}
}
if (!rule->m_tag.empty() && !tags.isTagged(rule->m_tag))
continue;
if (!rule->m_class.empty() && !rule->m_classRegex.passes(pWindow->m_class))
continue;
if (!rule->m_title.empty() && !rule->m_titleRegex.passes(pWindow->m_title))
continue;
if (!rule->m_initialTitle.empty() && !rule->m_initialTitleRegex.passes(pWindow->m_initialTitle))
continue;
if (!rule->m_initialClass.empty() && !rule->m_initialClassRegex.passes(pWindow->m_initialClass))
continue;
} catch (std::exception& e) {
Debug::log(ERR, "Regex error at {} ({})", rule->m_value, e.what());
continue;
}
}
// applies. Read the rule and behave accordingly
Debug::log(LOG, "Window rule {} -> {} matched {}", rule->m_rule, rule->m_value, pWindow);
returns.emplace_back(rule);
// apply tag with local tags
if (rule->m_ruleType == CWindowRule::RULE_TAG) {
CVarList vars{rule->m_rule, 0, 's', true};
if (vars.size() == 2 && vars[0] == "tag")
tags.applyTag(vars[1], true);
}
if (dynamic)
continue;
if (rule->m_rule == "float")
hasFloating = true;
else if (rule->m_rule == "fullscreen")
hasFullscreen = true;
}
std::vector<uint64_t> PIDs = {sc<uint64_t>(pWindow->getPID())};
while (getPPIDof(PIDs.back()) > 10)
PIDs.push_back(getPPIDof(PIDs.back()));
bool anyExecFound = false;
for (auto const& er : m_execRequestedRules) {
if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) {
returns.emplace_back(makeShared<CWindowRule>(er.szRule, "", false, true));
anyExecFound = true;
}
}
if (anyExecFound && !shadowExec) // remove exec rules to unclog searches in the future, why have the garbage here.
std::erase_if(m_execRequestedRules, [&](const SExecRequestedRule& other) { return std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == other.iPid; }); });
return returns;
}
std::vector<SP<CLayerRule>> CConfigManager::getMatchingRules(PHLLS pLS) {
std::vector<SP<CLayerRule>> returns;
if (!pLS->m_layerSurface || pLS->m_fadingOut)
return returns;
for (auto const& lr : m_layerRules) {
if (lr->m_targetNamespace.starts_with("address:0x")) {
if (std::format("address:0x{:x}", rc<uintptr_t>(pLS.get())) != lr->m_targetNamespace)
continue;
} else if (!lr->m_targetNamespaceRegex.passes(pLS->m_layerSurface->m_layerNamespace))
continue;
// hit
returns.emplace_back(lr);
}
if (shouldBlurLS(pLS->m_layerSurface->m_layerNamespace))
returns.emplace_back(makeShared<CLayerRule>(pLS->m_layerSurface->m_layerNamespace, "blur"));
return returns;
}
void CConfigManager::dispatchExecOnce() { void CConfigManager::dispatchExecOnce() {
if (m_firstExecDispatched || m_isFirstLaunch) if (m_firstExecDispatched || m_isFirstLaunch)
return; return;
@ -1831,16 +1709,6 @@ bool CConfigManager::deviceConfigExists(const std::string& dev) {
return m_config->specialCategoryExistsForKey("device", copy.c_str()); return m_config->specialCategoryExistsForKey("device", copy.c_str());
} }
bool CConfigManager::shouldBlurLS(const std::string& ns) {
for (auto const& bls : m_blurLSNamespaces) {
if (bls == ns) {
return true;
}
}
return false;
}
void CConfigManager::ensureMonitorStatus() { void CConfigManager::ensureMonitorStatus() {
for (auto const& rm : g_pCompositor->m_realMonitors) { for (auto const& rm : g_pCompositor->m_realMonitors) {
if (!rm->m_output || rm->m_isUnsafeFallback) if (!rm->m_output || rm->m_isUnsafeFallback)
@ -1894,7 +1762,7 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) {
return; // ??? return; // ???
bool wantVRR = PWORKSPACE->m_hasFullscreenWindow && (PWORKSPACE->m_fullscreenMode & FSMODE_FULLSCREEN); bool wantVRR = PWORKSPACE->m_hasFullscreenWindow && (PWORKSPACE->m_fullscreenMode & FSMODE_FULLSCREEN);
if (wantVRR && PWORKSPACE->getFullscreenWindow()->m_windowData.noVRR.valueOrDefault()) if (wantVRR && PWORKSPACE->getFullscreenWindow()->m_ruleApplicator->noVRR().valueOrDefault())
wantVRR = false; wantVRR = false;
if (wantVRR && USEVRR == 3) { if (wantVRR && USEVRR == 3) {
@ -1963,10 +1831,6 @@ const std::vector<SWorkspaceRule>& CConfigManager::getAllWorkspaceRules() {
return m_workspaceRules; return m_workspaceRules;
} }
void CConfigManager::addExecRule(const SExecRequestedRule& rule) {
m_execRequestedRules.push_back(rule);
}
void CConfigManager::handlePluginLoads() { void CConfigManager::handlePluginLoads() {
if (!g_pPluginSystem) if (!g_pPluginSystem)
return; return;
@ -2675,239 +2539,6 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma
return {}; return {};
} }
std::optional<std::string> CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
const auto VARLIST = CVarList(value, 0, ',', true);
std::vector<std::string_view> tokens;
std::unordered_map<std::string_view, std::string_view> params;
bool parsingParams = false;
for (const auto& varStr : VARLIST) {
std::string_view var = varStr;
auto sep = var.find(':');
std::string_view key = (sep != std::string_view::npos) ? var.substr(0, sep) : var;
bool isParam = (sep != std::string_view::npos && !(key.starts_with("workspace ") || (key.starts_with("monitor ")) || key.ends_with("plugin")));
if (!parsingParams) {
if (!isParam) {
tokens.emplace_back(var);
continue;
}
parsingParams = true;
}
if (sep == std::string_view::npos)
return std::format("Invalid rule: {}, Invalid parameter: {}", value, std::string(var));
auto pos = var.find_first_not_of(' ', sep + 1);
std::string_view val = (pos != std::string_view::npos) ? var.substr(pos) : std::string_view{};
params[key] = val;
}
auto get = [&](std::string_view key) -> std::string_view {
if (auto it = params.find(key); it != params.end())
return it->second;
return {};
};
auto applyParams = [&](SP<CWindowRule> rule) -> bool {
bool set = false;
if (auto v = get("class"); !v.empty()) {
set |= (rule->m_class = v, true);
rule->m_classRegex = {std::string(v)};
}
if (auto v = get("title"); !v.empty()) {
set |= (rule->m_title = v, true);
rule->m_titleRegex = {std::string(v)};
}
if (auto v = get("tag"); !v.empty())
set |= (rule->m_tag = v, true);
if (auto v = get("initialClass"); !v.empty()) {
set |= (rule->m_initialClass = v, true);
rule->m_initialClassRegex = {std::string(v)};
}
if (auto v = get("initialTitle"); !v.empty()) {
set |= (rule->m_initialTitle = v, true);
rule->m_initialTitleRegex = {std::string(v)};
}
if (auto v = get("xwayland"); !v.empty())
set |= (rule->m_X11 = (v == "1"), true);
if (auto v = get("floating"); !v.empty())
set |= (rule->m_floating = (v == "1"), true);
if (auto v = get("fullscreen"); !v.empty())
set |= (rule->m_fullscreen = (v == "1"), true);
if (auto v = get("pinned"); !v.empty())
set |= (rule->m_pinned = (v == "1"), true);
if (auto v = get("focus"); !v.empty())
set |= (rule->m_focus = (v == "1"), true);
if (auto v = get("group"); !v.empty())
set |= (rule->m_group = (v == "1"), true);
if (auto v = get("modal"); !v.empty())
set |= (rule->m_modal = (v == "1"), true);
if (auto v = get("fullscreenstate"); !v.empty())
set |= (rule->m_fullscreenState = v, true);
if (auto v = get("workspace"); !v.empty())
set |= (rule->m_workspace = v, true);
if (auto v = get("onworkspace"); !v.empty())
set |= (rule->m_onWorkspace = v, true);
if (auto v = get("content"); !v.empty())
set |= (rule->m_contentType = v, true);
if (auto v = get("xdgTag"); !v.empty())
set |= (rule->m_xdgTag = v, true);
return set;
};
std::vector<SP<CWindowRule>> rules;
for (auto token : tokens) {
if (token.starts_with("unset")) {
std::string ruleName = "";
if (token.size() <= 6 || token.contains("all"))
ruleName = "all";
else
ruleName = std::string(token.substr(6));
auto rule = makeShared<CWindowRule>(ruleName, value, true);
applyParams(rule);
std::erase_if(m_windowRules, [&](const auto& other) {
if (!other->m_v2)
return other->m_class == rule->m_class && !rule->m_class.empty();
if (rule->m_ruleType != other->m_ruleType && ruleName != "all")
return false;
if (!rule->m_tag.empty() && rule->m_tag != other->m_tag)
return false;
if (!rule->m_class.empty() && rule->m_class != other->m_class)
return false;
if (!rule->m_title.empty() && rule->m_title != other->m_title)
return false;
if (!rule->m_initialClass.empty() && rule->m_initialClass != other->m_initialClass)
return false;
if (!rule->m_initialTitle.empty() && rule->m_initialTitle != other->m_initialTitle)
return false;
if (rule->m_X11 != -1 && rule->m_X11 != other->m_X11)
return false;
if (rule->m_floating != -1 && rule->m_floating != other->m_floating)
return false;
if (rule->m_fullscreen != -1 && rule->m_fullscreen != other->m_fullscreen)
return false;
if (rule->m_pinned != -1 && rule->m_pinned != other->m_pinned)
return false;
if (!rule->m_fullscreenState.empty() && rule->m_fullscreenState != other->m_fullscreenState)
return false;
if (!rule->m_workspace.empty() && rule->m_workspace != other->m_workspace)
return false;
if (rule->m_focus != -1 && rule->m_focus != other->m_focus)
return false;
if (!rule->m_onWorkspace.empty() && rule->m_onWorkspace != other->m_onWorkspace)
return false;
if (!rule->m_contentType.empty() && rule->m_contentType != other->m_contentType)
return false;
if (rule->m_group != -1 && rule->m_group != other->m_group)
return false;
if (rule->m_modal != -1 && rule->m_modal != other->m_modal)
return false;
return true;
});
} else {
auto rule = makeShared<CWindowRule>(std::string(token), value, true);
if (rule->m_ruleType == CWindowRule::RULE_INVALID) {
Debug::log(ERR, "Invalid rule found: {}, Invalid value: {}", value, token);
return std::format("Invalid rule found: {}, Invalid value: {}", value, token);
}
if (applyParams(rule))
rules.emplace_back(rule);
else {
Debug::log(INFO, "===== Skipping rule: {}, Invalid parameters", rule->m_value);
return std::format("Invalid parameters found in: {}", value);
}
}
}
if (rules.empty() && tokens.empty())
return "Invalid rule syntax: no rules provided";
for (auto& rule : rules) {
if (rule->m_ruleType == CWindowRule::RULE_SIZE || rule->m_ruleType == CWindowRule::RULE_MAXSIZE || rule->m_ruleType == CWindowRule::RULE_MINSIZE)
m_windowRules.insert(m_windowRules.begin(), rule);
else
m_windowRules.emplace_back(rule);
}
return {};
}
std::optional<std::string> CConfigManager::handleLayerRule(const std::string& command, const std::string& value) {
const auto RULE = trim(value.substr(0, value.find_first_of(',')));
const auto VALUE = trim(value.substr(value.find_first_of(',') + 1));
// check rule and value
if (RULE.empty() || VALUE.empty())
return "empty rule?";
if (RULE == "unset") {
std::erase_if(m_layerRules, [&](const auto& other) { return other->m_targetNamespace == VALUE; });
return {};
}
auto rule = makeShared<CLayerRule>(RULE, VALUE);
if (rule->m_ruleType == CLayerRule::RULE_INVALID) {
Debug::log(ERR, "Invalid rule found: {}", RULE);
return "Invalid rule found: " + RULE;
}
rule->m_targetNamespaceRegex = {VALUE};
m_layerRules.emplace_back(rule);
for (auto const& m : g_pCompositor->m_monitors)
for (auto const& lsl : m->m_layerSurfaceLayers)
for (auto const& ls : lsl)
ls->applyRules();
return {};
}
void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBlur) {
const bool BYADDRESS = name.starts_with("address:");
std::string matchName = name;
if (BYADDRESS)
matchName = matchName.substr(8);
for (auto const& m : g_pCompositor->m_monitors) {
for (auto const& lsl : m->m_layerSurfaceLayers) {
for (auto const& ls : lsl) {
if (BYADDRESS) {
if (std::format("0x{:x}", rc<uintptr_t>(ls.get())) == matchName)
ls->m_forceBlur = forceBlur;
} else if (ls->m_namespace == matchName)
ls->m_forceBlur = forceBlur;
}
}
}
}
std::optional<std::string> CConfigManager::handleBlurLS(const std::string& command, const std::string& value) {
if (value.starts_with("remove,")) {
const auto TOREMOVE = trim(value.substr(7));
if (std::erase_if(m_blurLSNamespaces, [&](const auto& other) { return other == TOREMOVE; }))
updateBlurredLS(TOREMOVE, false);
return {};
}
m_blurLSNamespaces.emplace_back(value);
updateBlurredLS(value, true);
return {};
}
std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) { std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) {
// This can either be the monitor or the workspace identifier // This can either be the monitor or the workspace identifier
const auto FIRST_DELIM = value.find_first_of(','); const auto FIRST_DELIM = value.find_first_of(',');
@ -3228,6 +2859,82 @@ std::optional<std::string> CConfigManager::handleGesture(const std::string& comm
return std::nullopt; return std::nullopt;
} }
std::optional<std::string> CConfigManager::handleWindowrule(const std::string& command, const std::string& value) {
CVarList2 data(std::string{value}, 0, ',');
SP<Desktop::Rule::CWindowRule> rule = makeShared<Desktop::Rule::CWindowRule>();
const auto& PROPS = Desktop::Rule::allMatchPropStrings();
const auto& EFFECTS = Desktop::Rule::windowEffects()->allEffectStrings();
for (const auto& el : data) {
// split on space, no need for a CVarList here
size_t spacePos = el.find(' ');
if (spacePos == std::string::npos)
return std::format("invalid field {}: missing a value", el);
const bool FIRST_IS_PROP = el.starts_with("match:");
const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos);
if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) {
// it's a prop
const auto PROP = Desktop::Rule::matchPropFromString(FIRST);
if (!PROP.has_value())
return std::format("invalid prop {}", el);
rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)});
} else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) {
// it's an effect
const auto EFFECT = Desktop::Rule::windowEffects()->get(FIRST);
if (!EFFECT.has_value())
return std::format("invalid effect {}", el);
rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
} else
return std::format("invalid field type {}", FIRST);
}
m_keywordRules.emplace_back(std::move(rule));
if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.isDynamicKeyword)
Desktop::Rule::ruleEngine()->registerRule(SP<Desktop::Rule::IRule>{m_keywordRules.back()});
return std::nullopt;
}
std::optional<std::string> CConfigManager::handleLayerrule(const std::string& command, const std::string& value) {
CVarList2 data(std::string{value}, 0, ',');
SP<Desktop::Rule::CLayerRule> rule = makeShared<Desktop::Rule::CLayerRule>();
const auto& PROPS = Desktop::Rule::allMatchPropStrings();
const auto& EFFECTS = Desktop::Rule::layerEffects()->allEffectStrings();
for (const auto& el : data) {
// split on space, no need for a CVarList here
size_t spacePos = el.find(' ');
if (spacePos == std::string::npos)
return std::format("invalid field {}: missing a value", el);
const bool FIRST_IS_PROP = el.starts_with("match:");
const auto FIRST = FIRST_IS_PROP ? el.substr(6, spacePos - 6) : el.substr(0, spacePos);
if (FIRST_IS_PROP && std::ranges::contains(PROPS, FIRST)) {
// it's a prop
const auto PROP = Desktop::Rule::matchPropFromString(FIRST);
if (!PROP.has_value())
return std::format("invalid prop {}", el);
rule->registerMatch(*PROP, std::string{el.substr(spacePos + 1)});
} else if (!FIRST_IS_PROP && std::ranges::contains(EFFECTS, FIRST)) {
// it's an effect
const auto EFFECT = Desktop::Rule::layerEffects()->get(FIRST);
if (!EFFECT.has_value())
return std::format("invalid effect {}", el);
rule->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
} else
return std::format("invalid field type {}", FIRST);
}
m_keywordRules.emplace_back(std::move(rule));
return std::nullopt;
}
const std::vector<SConfigOptionDescription>& CConfigManager::getAllDescriptions() { const std::vector<SConfigOptionDescription>& CConfigManager::getAllDescriptions() {
return CONFIG_OPTIONS; return CONFIG_OPTIONS;
} }

View file

@ -13,14 +13,12 @@
#include <xf86drmMode.h> #include <xf86drmMode.h>
#include "../helpers/Monitor.hpp" #include "../helpers/Monitor.hpp"
#include "../desktop/Window.hpp" #include "../desktop/Window.hpp"
#include "../desktop/LayerRule.hpp"
#include "ConfigDataValues.hpp" #include "ConfigDataValues.hpp"
#include "../SharedDefs.hpp" #include "../SharedDefs.hpp"
#include "../helpers/Color.hpp" #include "../helpers/Color.hpp"
#include "../desktop/DesktopTypes.hpp" #include "../desktop/DesktopTypes.hpp"
#include "../helpers/memory/Memory.hpp" #include "../helpers/memory/Memory.hpp"
#include "../desktop/WindowRule.hpp"
#include "../managers/XWaylandManager.hpp" #include "../managers/XWaylandManager.hpp"
#include "../managers/KeybindManager.hpp" #include "../managers/KeybindManager.hpp"
@ -68,11 +66,6 @@ struct SPluginVariable {
std::string name = ""; std::string name = "";
}; };
struct SExecRequestedRule {
std::string szRule = "";
uint64_t iPid = 0;
};
enum eConfigOptionType : uint8_t { enum eConfigOptionType : uint8_t {
CONFIG_OPTION_BOOL = 0, CONFIG_OPTION_BOOL = 0,
CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/ CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/
@ -214,7 +207,6 @@ class CConfigManager {
bool deviceConfigExplicitlySet(const std::string&, const std::string&); bool deviceConfigExplicitlySet(const std::string&, const std::string&);
bool deviceConfigExists(const std::string&); bool deviceConfigExists(const std::string&);
Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback); Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback);
bool shouldBlurLS(const std::string&);
void* const* getConfigValuePtr(const std::string&); void* const* getConfigValuePtr(const std::string&);
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = ""); Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
@ -229,8 +221,6 @@ class CConfigManager {
std::string getBoundMonitorStringForWS(const std::string&); std::string getBoundMonitorStringForWS(const std::string&);
const std::vector<SWorkspaceRule>& getAllWorkspaceRules(); const std::vector<SWorkspaceRule>& getAllWorkspaceRules();
std::vector<SP<CWindowRule>> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
std::vector<SP<CLayerRule>> getMatchingRules(PHLLS);
void ensurePersistentWorkspacesPresent(); void ensurePersistentWorkspacesPresent();
const std::vector<SConfigOptionDescription>& getAllDescriptions(); const std::vector<SConfigOptionDescription>& getAllDescriptions();
@ -260,8 +250,6 @@ class CConfigManager {
SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&); SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&);
void addExecRule(const SExecRequestedRule&);
void handlePluginLoads(); void handlePluginLoads();
std::string getErrors(); std::string getErrors();
@ -274,22 +262,24 @@ class CConfigManager {
std::optional<std::string> handleMonitor(const std::string&, const std::string&); std::optional<std::string> handleMonitor(const std::string&, const std::string&);
std::optional<std::string> handleBind(const std::string&, const std::string&); std::optional<std::string> handleBind(const std::string&, const std::string&);
std::optional<std::string> handleUnbind(const std::string&, const std::string&); std::optional<std::string> handleUnbind(const std::string&, const std::string&);
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&); std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
std::optional<std::string> handleBezier(const std::string&, const std::string&); std::optional<std::string> handleBezier(const std::string&, const std::string&);
std::optional<std::string> handleAnimation(const std::string&, const std::string&); std::optional<std::string> handleAnimation(const std::string&, const std::string&);
std::optional<std::string> handleSource(const std::string&, const std::string&); std::optional<std::string> handleSource(const std::string&, const std::string&);
std::optional<std::string> handleSubmap(const std::string&, const std::string&); std::optional<std::string> handleSubmap(const std::string&, const std::string&);
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
std::optional<std::string> handleBindWS(const std::string&, const std::string&); std::optional<std::string> handleBindWS(const std::string&, const std::string&);
std::optional<std::string> handleEnv(const std::string&, const std::string&); std::optional<std::string> handleEnv(const std::string&, const std::string&);
std::optional<std::string> handlePlugin(const std::string&, const std::string&); std::optional<std::string> handlePlugin(const std::string&, const std::string&);
std::optional<std::string> handlePermission(const std::string&, const std::string&); std::optional<std::string> handlePermission(const std::string&, const std::string&);
std::optional<std::string> handleGesture(const std::string&, const std::string&); std::optional<std::string> handleGesture(const std::string&, const std::string&);
std::optional<std::string> handleWindowrule(const std::string&, const std::string&);
std::optional<std::string> handleLayerrule(const std::string&, const std::string&);
std::optional<std::string> handleMonitorv2(const std::string& output); std::optional<std::string> handleMonitorv2(const std::string& output);
Hyprlang::CParseResult handleMonitorv2(); Hyprlang::CParseResult handleMonitorv2();
std::optional<std::string> addRuleFromConfigKey(const std::string& name);
std::optional<std::string> addLayerRuleFromConfigKey(const std::string& name);
Hyprlang::CParseResult reloadRules();
std::string m_configCurrentPath; std::string m_configCurrentPath;
@ -310,19 +300,16 @@ class CConfigManager {
SSubmap m_currentSubmap; SSubmap m_currentSubmap;
std::vector<SExecRequestedRule> m_execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
std::vector<std::string> m_declaredPlugins; std::vector<std::string> m_declaredPlugins;
std::vector<SPluginKeyword> m_pluginKeywords; std::vector<SPluginKeyword> m_pluginKeywords;
std::vector<SPluginVariable> m_pluginVariables; std::vector<SPluginVariable> m_pluginVariables;
std::vector<SP<Desktop::Rule::IRule>> m_keywordRules;
bool m_isFirstLaunch = true; // For exec-once bool m_isFirstLaunch = true; // For exec-once
std::vector<SMonitorRule> m_monitorRules; std::vector<SMonitorRule> m_monitorRules;
std::vector<SWorkspaceRule> m_workspaceRules; std::vector<SWorkspaceRule> m_workspaceRules;
std::vector<SP<CWindowRule>> m_windowRules;
std::vector<SP<CLayerRule>> m_layerRules;
std::vector<std::string> m_blurLSNamespaces;
bool m_firstExecDispatched = false; bool m_firstExecDispatched = false;
bool m_manualCrashInitiated = false; bool m_manualCrashInitiated = false;
@ -336,11 +323,11 @@ class CConfigManager {
uint32_t m_configValueNumber = 0; uint32_t m_configValueNumber = 0;
// internal methods // internal methods
void updateBlurredLS(const std::string&, const bool);
void setDefaultAnimationVars(); void setDefaultAnimationVars();
std::optional<std::string> resetHLConfig(); std::optional<std::string> resetHLConfig();
std::optional<std::string> generateConfig(std::string configPath); std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> verifyConfigExists(); std::optional<std::string> verifyConfigExists();
void reloadRuleConfigs();
void postConfigReload(const Hyprlang::CParseResult& result); void postConfigReload(const Hyprlang::CParseResult& result);
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&); SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);

View file

@ -1,5 +1,7 @@
#include "ConfigWatcher.hpp" #include "ConfigWatcher.hpp"
#if defined(__linux__)
#include <linux/limits.h> #include <linux/limits.h>
#endif
#include <sys/inotify.h> #include <sys/inotify.h>
#include "../debug/Log.hpp" #include "../debug/Log.hpp"
#include <ranges> #include <ranges>

View file

@ -44,6 +44,7 @@ using namespace Hyprutils::OS;
#include "config/ConfigManager.hpp" #include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp" #include "helpers/MiscFunctions.hpp"
#include "../desktop/LayerSurface.hpp" #include "../desktop/LayerSurface.hpp"
#include "../desktop/rule/Engine.hpp"
#include "../version.h" #include "../version.h"
#include "../Compositor.hpp" #include "../Compositor.hpp"
@ -317,7 +318,7 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ
} }
static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) { static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
const auto tags = w->m_tags.getTags(); const auto tags = w->m_ruleApplicator->m_tagKeeper.getTags();
if (format == eHyprCtlOutputFormat::FORMAT_JSON) if (format == eHyprCtlOutputFormat::FORMAT_JSON)
return std::ranges::fold_left(tags, std::string(), return std::ranges::fold_left(tags, std::string(),
@ -1272,8 +1273,12 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
if (COMMAND.empty()) if (COMMAND.empty())
return "Invalid input: command is empty"; return "Invalid input: command is empty";
g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = true;
std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE); std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE);
g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = false;
// if we are executing a dynamic source we have to reload everything, so every if will have a check for source. // if we are executing a dynamic source we have to reload everything, so every if will have a check for source.
if (COMMAND == "monitor" || COMMAND == "source") if (COMMAND == "monitor" || COMMAND == "source")
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
@ -1306,8 +1311,7 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
g_pConfigManager->updateWatcher(); g_pConfigManager->updateWatcher();
// decorations will probably need a repaint // decorations will probably need a repaint
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" || if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") {
COMMAND.starts_with("windowrule")) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor"); static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
for (auto const& m : g_pCompositor->m_monitors) { for (auto const& m : g_pCompositor->m_monitors) {
*(m->m_cursorZoom) = *PZOOMFACTOR; *(m->m_cursorZoom) = *PZOOMFACTOR;
@ -1316,6 +1320,9 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
} }
} }
if (COMMAND.contains("windowrule ") || COMMAND.contains("windowrule["))
g_pConfigManager->reloadRules();
if (COMMAND.contains("workspace")) if (COMMAND.contains("workspace"))
g_pConfigManager->ensurePersistentWorkspacesPresent(); g_pConfigManager->ensurePersistentWorkspacesPresent();
@ -1521,11 +1528,6 @@ static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string req
return "ok"; return "ok";
} }
static std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
auto result = g_pKeybindManager->m_dispatchers["setprop"](request.substr(request.find_first_of(' ') + 1));
return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error);
}
static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) { static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
@ -1543,9 +1545,9 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const bool FORMNORM = format == FORMAT_NORMAL; const bool FORMNORM = format == FORMAT_NORMAL;
auto sizeToString = [&](bool max) -> std::string { auto sizeToString = [&](bool max) -> std::string {
auto sizeValue = PWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)); auto sizeValue = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
if (max) if (max)
sizeValue = PWINDOW->m_windowData.maxSize.valueOr(Vector2D(INFINITY, INFINITY)); sizeValue = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D(INFINITY, INFINITY));
if (FORMNORM) if (FORMNORM)
return std::format("{} {}", sizeValue.x, sizeValue.y); return std::format("{} {}", sizeValue.x, sizeValue.y);
@ -1556,7 +1558,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
} }
}; };
auto alphaToString = [&](CWindowOverridableVar<SAlphaValue>& alpha, bool getAlpha) -> std::string { auto alphaToString = [&](Desktop::Types::COverridableVar<Desktop::Types::SAlphaValue>& alpha, bool getAlpha) -> std::string {
if (FORMNORM) { if (FORMNORM) {
if (getAlpha) if (getAlpha)
return std::format("{}", alpha.valueOrDefault().alpha); return std::format("{}", alpha.valueOrDefault().alpha);
@ -1590,7 +1592,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const auto* const ACTIVECOLOR = const auto* const ACTIVECOLOR =
!PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL); !PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
std::string borderColorString = PWINDOW->m_windowData.activeBorderColor.valueOr(*ACTIVECOLOR).toString(); std::string borderColorString = PWINDOW->m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR).toString();
if (FORMNORM) if (FORMNORM)
return borderColorString; return borderColorString;
else else
@ -1603,7 +1605,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const auto* const INACTIVECOLOR = !PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : const auto* const INACTIVECOLOR = !PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) :
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL); (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
std::string borderColorString = PWINDOW->m_windowData.inactiveBorderColor.valueOr(*INACTIVECOLOR).toString(); std::string borderColorString = PWINDOW->m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR).toString();
if (FORMNORM) if (FORMNORM)
return borderColorString; return borderColorString;
else else
@ -1618,38 +1620,92 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault()); return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
}; };
if (PROP == "animationstyle") { if (PROP == "animation") {
auto& animationStyle = PWINDOW->m_windowData.animationStyle; auto& animationStyle = PWINDOW->m_ruleApplicator->animationStyle();
if (FORMNORM) if (FORMNORM)
return animationStyle.valueOr("(unset)"); return animationStyle.valueOr("(unset)");
else else
return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr("")); return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
} else if (PROP == "maxsize") } else if (PROP == "max_size")
return sizeToString(true); return sizeToString(true);
else if (PROP == "minsize") else if (PROP == "min_size")
return sizeToString(false); return sizeToString(false);
else if (PROP == "alpha") else if (PROP == "opacity")
return alphaToString(PWINDOW->m_windowData.alpha, true); return alphaToString(PWINDOW->m_ruleApplicator->alpha(), true);
else if (PROP == "alphainactive") else if (PROP == "opacity_inactive")
return alphaToString(PWINDOW->m_windowData.alphaInactive, true); return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), true);
else if (PROP == "alphafullscreen") else if (PROP == "opacity_fullscreen")
return alphaToString(PWINDOW->m_windowData.alphaFullscreen, true); return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), true);
else if (PROP == "alphaoverride") else if (PROP == "opacity_override")
return alphaToString(PWINDOW->m_windowData.alpha, false); return alphaToString(PWINDOW->m_ruleApplicator->alpha(), false);
else if (PROP == "alphainactiveoverride") else if (PROP == "opacity_inactive_override")
return alphaToString(PWINDOW->m_windowData.alphaInactive, false); return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), false);
else if (PROP == "alphafullscreenoverride") else if (PROP == "opacity_fullscreen_override")
return alphaToString(PWINDOW->m_windowData.alphaFullscreen, false); return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), false);
else if (PROP == "activebordercolor") else if (PROP == "active_border_color")
return borderColorToString(true); return borderColorToString(true);
else if (PROP == "inactivebordercolor") else if (PROP == "inactive_border_color")
return borderColorToString(false); return borderColorToString(false);
else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end()) else if (PROP == "allows_input")
return windowPropToString(*search->second(PWINDOW)); return windowPropToString(PWINDOW->m_ruleApplicator->allowsInput());
else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end()) else if (PROP == "decorate")
return windowPropToString(*search->second(PWINDOW)); return windowPropToString(PWINDOW->m_ruleApplicator->decorate());
else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end()) else if (PROP == "focus_on_activate")
return windowPropToString(*search->second(PWINDOW)); return windowPropToString(PWINDOW->m_ruleApplicator->focusOnActivate());
else if (PROP == "keep_aspect_ratio")
return windowPropToString(PWINDOW->m_ruleApplicator->keepAspectRatio());
else if (PROP == "nearest_neighbor")
return windowPropToString(PWINDOW->m_ruleApplicator->nearestNeighbor());
else if (PROP == "no_anim")
return windowPropToString(PWINDOW->m_ruleApplicator->noAnim());
else if (PROP == "no_blur")
return windowPropToString(PWINDOW->m_ruleApplicator->noBlur());
else if (PROP == "no_dim")
return windowPropToString(PWINDOW->m_ruleApplicator->noDim());
else if (PROP == "no_focus")
return windowPropToString(PWINDOW->m_ruleApplicator->noFocus());
else if (PROP == "no_max_size")
return windowPropToString(PWINDOW->m_ruleApplicator->noMaxSize());
else if (PROP == "no_shadow")
return windowPropToString(PWINDOW->m_ruleApplicator->noShadow());
else if (PROP == "no_shortcuts_inhibit")
return windowPropToString(PWINDOW->m_ruleApplicator->noShortcutsInhibit());
else if (PROP == "opaque")
return windowPropToString(PWINDOW->m_ruleApplicator->opaque());
else if (PROP == "dim_around")
return windowPropToString(PWINDOW->m_ruleApplicator->dimAround());
else if (PROP == "force_rgbx")
return windowPropToString(PWINDOW->m_ruleApplicator->RGBX());
else if (PROP == "sync_fullscreen")
return windowPropToString(PWINDOW->m_ruleApplicator->syncFullscreen());
else if (PROP == "immediate")
return windowPropToString(PWINDOW->m_ruleApplicator->tearing());
else if (PROP == "xray")
return windowPropToString(PWINDOW->m_ruleApplicator->xray());
else if (PROP == "render_unfocused")
return windowPropToString(PWINDOW->m_ruleApplicator->renderUnfocused());
else if (PROP == "no_follow_mouse")
return windowPropToString(PWINDOW->m_ruleApplicator->noFollowMouse());
else if (PROP == "no_screen_share")
return windowPropToString(PWINDOW->m_ruleApplicator->noScreenShare());
else if (PROP == "no_vrr")
return windowPropToString(PWINDOW->m_ruleApplicator->noVRR());
else if (PROP == "persistent_size")
return windowPropToString(PWINDOW->m_ruleApplicator->persistentSize());
else if (PROP == "stay_focused")
return windowPropToString(PWINDOW->m_ruleApplicator->stayFocused());
else if (PROP == "idle_inhibit")
return windowPropToString(PWINDOW->m_ruleApplicator->idleInhibitMode());
else if (PROP == "border_size")
return windowPropToString(PWINDOW->m_ruleApplicator->borderSize());
else if (PROP == "rounding")
return windowPropToString(PWINDOW->m_ruleApplicator->rounding());
else if (PROP == "rounding_power")
return windowPropToString(PWINDOW->m_ruleApplicator->roundingPower());
else if (PROP == "scroll_mouse")
return windowPropToString(PWINDOW->m_ruleApplicator->scrollMouse());
else if (PROP == "scroll_touchpad")
return windowPropToString(PWINDOW->m_ruleApplicator->scrollTouchpad());
return "prop not found"; return "prop not found";
} }
@ -2014,7 +2070,6 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin}); registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify}); registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify}); registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp}); registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp});
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror}); registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest}); registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
@ -2130,8 +2185,7 @@ std::string CHyprCtl::getReply(std::string request) {
if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible()) if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible())
continue; continue;
w->updateDynamicRules(); Desktop::Rule::ruleEngine()->updateAllRules();
g_pCompositor->updateWindowAnimatedDecorationValues(w);
} }
for (auto const& m : g_pCompositor->m_monitors) { for (auto const& m : g_pCompositor->m_monitors) {

View file

@ -27,6 +27,7 @@ class CHyprCtl {
struct { struct {
bool all = false; bool all = false;
bool sysInfoConfig = false; bool sysInfoConfig = false;
bool isDynamicKeyword = false;
pid_t pid = 0; pid_t pid = 0;
SP<CPromise<std::string>> pendingPromise; SP<CPromise<std::string>> pendingPromise;
} m_currentRequestParams; } m_currentRequestParams;

View file

@ -1,42 +0,0 @@
#include <re2/re2.h>
#include "LayerRule.hpp"
#include <unordered_set>
#include <algorithm>
#include "../debug/Log.hpp"
static const auto RULES = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround", "noscreenshare"};
static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order", "abovelock"};
CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : m_targetNamespace(ns_), m_rule(rule_) {
const bool VALID = RULES.contains(m_rule) || std::ranges::any_of(RULES_PREFIX, [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
if (!VALID)
return;
if (m_rule == "noanim")
m_ruleType = RULE_NOANIM;
else if (m_rule == "blur")
m_ruleType = RULE_BLUR;
else if (m_rule == "blurpopups")
m_ruleType = RULE_BLURPOPUPS;
else if (m_rule == "dimaround")
m_ruleType = RULE_DIMAROUND;
else if (m_rule == "noscreenshare")
m_ruleType = RULE_NOSCREENSHARE;
else if (m_rule.starts_with("ignorealpha"))
m_ruleType = RULE_IGNOREALPHA;
else if (m_rule.starts_with("ignorezero"))
m_ruleType = RULE_IGNOREZERO;
else if (m_rule.starts_with("xray"))
m_ruleType = RULE_XRAY;
else if (m_rule.starts_with("animation"))
m_ruleType = RULE_ANIMATION;
else if (m_rule.starts_with("order"))
m_ruleType = RULE_ORDER;
else if (m_rule.starts_with("abovelock"))
m_ruleType = RULE_ABOVELOCK;
else {
Debug::log(ERR, "CLayerRule: didn't match a rule that was found valid?!");
m_ruleType = RULE_INVALID;
}
}

View file

@ -1,33 +0,0 @@
#pragma once
#include <string>
#include <cstdint>
#include "Rule.hpp"
class CLayerRule {
public:
CLayerRule(const std::string& rule, const std::string& targetNS);
enum eRuleType : uint8_t {
RULE_INVALID = 0,
RULE_NOANIM,
RULE_BLUR,
RULE_BLURPOPUPS,
RULE_DIMAROUND,
RULE_ABOVELOCK,
RULE_IGNOREALPHA,
RULE_IGNOREZERO,
RULE_XRAY,
RULE_ANIMATION,
RULE_ORDER,
RULE_ZUMBA,
RULE_NOSCREENSHARE
};
eRuleType m_ruleType = RULE_INVALID;
const std::string m_targetNamespace;
const std::string m_rule;
CRuleRegexContainer m_targetNamespaceRegex;
};

View file

@ -37,7 +37,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
pLS->m_monitor = pMonitor; pLS->m_monitor = pMonitor;
pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS); pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS);
pLS->m_forceBlur = g_pConfigManager->shouldBlurLS(pLS->m_namespace); pLS->m_ruleApplicator = makeUnique<Desktop::Rule::CLayerRuleApplicator>(pLS);
g_pAnimationManager->createAnimation(0.f, pLS->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(0.f, pLS->m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadeLayersIn"), pLS, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(Vector2D(0, 0), pLS->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("layersIn"), pLS, AVARDAMAGE_ENTIRE);
@ -55,7 +55,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
void CLayerSurface::registerCallbacks() { void CLayerSurface::registerCallbacks() {
m_alpha->setUpdateCallback([this](auto) { m_alpha->setUpdateCallback([this](auto) {
if (m_dimAround && m_monitor) if (m_ruleApplicator->dimAround().valueOrDefault() && m_monitor)
g_pHyprRenderer->damageMonitor(m_monitor.lock()); g_pHyprRenderer->damageMonitor(m_monitor.lock());
}); });
} }
@ -137,6 +137,8 @@ void CLayerSurface::onMap() {
m_mapped = true; m_mapped = true;
m_interactivity = m_layerSurface->m_current.interactivity; m_interactivity = m_layerSurface->m_current.interactivity;
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL);
m_layerSurface->m_surface->map(); m_layerSurface->m_surface->map();
// this layer might be re-mapped. // this layer might be re-mapped.
@ -149,8 +151,6 @@ void CLayerSurface::onMap() {
if (!PMONITOR) if (!PMONITOR)
return; return;
applyRules();
PMONITOR->m_scheduledRecalc = true; PMONITOR->m_scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id); g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
@ -398,83 +398,6 @@ void CLayerSurface::onCommit() {
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform); g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
} }
void CLayerSurface::applyRules() {
m_noAnimations = false;
m_forceBlur = false;
m_ignoreAlpha = false;
m_dimAround = false;
m_noScreenShare = false;
m_ignoreAlphaValue = 0.f;
m_xray = -1;
m_animationStyle.reset();
for (auto const& rule : g_pConfigManager->getMatchingRules(m_self.lock())) {
switch (rule->m_ruleType) {
case CLayerRule::RULE_NOANIM: {
m_noAnimations = true;
break;
}
case CLayerRule::RULE_BLUR: {
m_forceBlur = true;
break;
}
case CLayerRule::RULE_BLURPOPUPS: {
m_forceBlurPopups = true;
break;
}
case CLayerRule::RULE_IGNOREALPHA:
case CLayerRule::RULE_IGNOREZERO: {
const auto FIRST_SPACE_POS = rule->m_rule.find_first_of(' ');
std::string alphaValue = "";
if (FIRST_SPACE_POS != std::string::npos)
alphaValue = rule->m_rule.substr(FIRST_SPACE_POS + 1);
try {
m_ignoreAlpha = true;
if (!alphaValue.empty())
m_ignoreAlphaValue = std::stof(alphaValue);
} catch (...) { Debug::log(ERR, "Invalid value passed to ignoreAlpha"); }
break;
}
case CLayerRule::RULE_DIMAROUND: {
m_dimAround = true;
break;
}
case CLayerRule::RULE_NOSCREENSHARE: {
m_noScreenShare = true;
break;
}
case CLayerRule::RULE_XRAY: {
CVarList vars{rule->m_rule, 0, ' '};
m_xray = configStringToInt(vars[1]).value_or(false);
break;
}
case CLayerRule::RULE_ANIMATION: {
CVarList vars{rule->m_rule, 2, 's'};
m_animationStyle = vars[1];
break;
}
case CLayerRule::RULE_ORDER: {
CVarList vars{rule->m_rule, 2, 's'};
try {
m_order = std::stoi(vars[1]);
} catch (...) { Debug::log(ERR, "Invalid value passed to order"); }
break;
}
case CLayerRule::RULE_ABOVELOCK: {
m_aboveLockscreen = true;
CVarList vars{rule->m_rule, 0, ' '};
m_aboveLockscreenInteractable = configStringToInt(vars[1]).value_or(false);
break;
}
default: break;
}
}
}
bool CLayerSurface::isFadedOut() { bool CLayerSurface::isFadedOut() {
if (!m_fadingOut) if (!m_fadingOut)
return false; return false;

View file

@ -3,6 +3,7 @@
#include <string> #include <string>
#include "../defines.hpp" #include "../defines.hpp"
#include "WLSurface.hpp" #include "WLSurface.hpp"
#include "rule/layerRule/LayerRuleApplicator.hpp"
#include "../helpers/AnimatedVariable.hpp" #include "../helpers/AnimatedVariable.hpp"
class CLayerShellResource; class CLayerShellResource;
@ -17,7 +18,6 @@ class CLayerSurface {
public: public:
~CLayerSurface(); ~CLayerSurface();
void applyRules();
bool isFadedOut(); bool isFadedOut();
int popupsCount(); int popupsCount();
@ -40,20 +40,8 @@ class CLayerSurface {
bool m_fadingOut = false; bool m_fadingOut = false;
bool m_readyToDelete = false; bool m_readyToDelete = false;
bool m_noProcess = false; bool m_noProcess = false;
bool m_noAnimations = false;
bool m_forceBlur = false; UP<Desktop::Rule::CLayerRuleApplicator> m_ruleApplicator;
bool m_forceBlurPopups = false;
int64_t m_xray = -1;
bool m_ignoreAlpha = false;
float m_ignoreAlphaValue = 0.f;
bool m_dimAround = false;
bool m_noScreenShare = false;
int64_t m_order = 0;
bool m_aboveLockscreen = false;
bool m_aboveLockscreenInteractable = false;
std::optional<std::string> m_animationStyle;
PHLLSREF m_self; PHLLSREF m_self;

View file

@ -1,22 +0,0 @@
#include <re2/re2.h>
#include "../helpers/memory/Memory.hpp"
#include "Rule.hpp"
#include "../debug/Log.hpp"
CRuleRegexContainer::CRuleRegexContainer(const std::string& regex_) {
const bool NEGATIVE = regex_.starts_with("negative:");
m_negative = NEGATIVE;
m_regex = makeUnique<RE2>(NEGATIVE ? regex_.substr(9) : regex_);
// TODO: maybe pop an error?
if (!m_regex->ok())
Debug::log(ERR, "RuleRegexContainer: regex {} failed to parse!", regex_);
}
bool CRuleRegexContainer::passes(const std::string& str) const {
if (!m_regex)
return false;
return RE2::FullMatch(str, *m_regex) != m_negative;
}

View file

@ -1,21 +0,0 @@
#pragma once
#include <hyprutils/memory/UniquePtr.hpp>
//NOLINTNEXTLINE
namespace re2 {
class RE2;
};
class CRuleRegexContainer {
public:
CRuleRegexContainer() = default;
CRuleRegexContainer(const std::string& regex);
bool passes(const std::string& str) const;
private:
Hyprutils::Memory::CUniquePointer<re2::RE2> m_regex;
bool m_negative = false;
};

View file

@ -24,6 +24,7 @@
#include "../protocols/FractionalScale.hpp" #include "../protocols/FractionalScale.hpp"
#include "../xwayland/XWayland.hpp" #include "../xwayland/XWayland.hpp"
#include "../helpers/Color.hpp" #include "../helpers/Color.hpp"
#include "../helpers/math/Expression.hpp"
#include "../events/Events.hpp" #include "../events/Events.hpp"
#include "../managers/XWaylandManager.hpp" #include "../managers/XWaylandManager.hpp"
#include "../render/Renderer.hpp" #include "../render/Renderer.hpp"
@ -43,6 +44,7 @@ PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
pWindow->m_self = pWindow; pWindow->m_self = pWindow;
pWindow->m_isX11 = true; pWindow->m_isX11 = true;
pWindow->m_ruleApplicator = makeUnique<Desktop::Rule::CWindowRuleApplicator>(pWindow);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
@ -67,6 +69,7 @@ PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) {
pWindow->m_self = pWindow; pWindow->m_self = pWindow;
resource->m_toplevel->m_window = pWindow; resource->m_toplevel->m_window = pWindow;
pWindow->m_ruleApplicator = makeUnique<Desktop::Rule::CWindowRuleApplicator>(pWindow);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realPosition, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); g_pAnimationManager->createAnimation(Vector2D(0, 0), pWindow->m_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
@ -138,7 +141,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
const int BORDERSIZE = getRealBorderSize(); const int BORDERSIZE = getRealBorderSize();
if (m_windowData.dimAround.valueOrDefault()) { if (m_ruleApplicator->dimAround().valueOrDefault()) {
if (const auto PMONITOR = m_monitor.lock(); PMONITOR) if (const auto PMONITOR = m_monitor.lock(); PMONITOR)
return {.topLeft = {m_realPosition->value().x - PMONITOR->m_position.x, m_realPosition->value().y - PMONITOR->m_position.y}, return {.topLeft = {m_realPosition->value().x - PMONITOR->m_position.x, m_realPosition->value().y - PMONITOR->m_position.y},
.bottomRight = {PMONITOR->m_size.x - (m_realPosition->value().x - PMONITOR->m_position.x), .bottomRight = {PMONITOR->m_size.x - (m_realPosition->value().x - PMONITOR->m_position.x),
@ -191,7 +194,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
} }
CBox CWindow::getFullWindowBoundingBox() { CBox CWindow::getFullWindowBoundingBox() {
if (m_windowData.dimAround.valueOrDefault()) { if (m_ruleApplicator->dimAround().valueOrDefault()) {
if (const auto PMONITOR = m_monitor.lock(); PMONITOR) if (const auto PMONITOR = m_monitor.lock(); PMONITOR)
return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y}; return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y};
} }
@ -251,7 +254,7 @@ SBoxExtents CWindow::getWindowExtentsUnified(uint64_t properties) {
} }
CBox CWindow::getWindowBoxUnified(uint64_t properties) { CBox CWindow::getWindowBoxUnified(uint64_t properties) {
if (m_windowData.dimAround.valueOrDefault()) { if (m_ruleApplicator->dimAround().valueOrDefault()) {
const auto PMONITOR = m_monitor.lock(); const auto PMONITOR = m_monitor.lock();
if (PMONITOR) if (PMONITOR)
return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y}; return {PMONITOR->m_position.x, PMONITOR->m_position.y, PMONITOR->m_size.x, PMONITOR->m_size.y};
@ -636,222 +639,6 @@ bool CWindow::isHidden() {
return m_hidden; return m_hidden;
} }
void CWindow::applyDynamicRule(const SP<CWindowRule>& r) {
const eOverridePriority priority = r->m_execRule ? PRIORITY_SET_PROP : PRIORITY_WINDOW_RULE;
static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
switch (r->m_ruleType) {
case CWindowRule::RULE_TAG: {
CVarList vars{r->m_rule, 0, 's', true};
if (vars.size() == 2 && vars[0] == "tag")
m_tags.applyTag(vars[1], true);
else
Debug::log(ERR, "Tag rule invalid: {}", r->m_rule);
break;
}
case CWindowRule::RULE_OPACITY: {
try {
CVarList vars(r->m_rule, 0, ' ');
int opacityIDX = 0;
for (auto const& r : vars) {
if (r == "opacity")
continue;
if (r == "override") {
if (opacityIDX == 1)
m_windowData.alpha = CWindowOverridableVar(SAlphaValue{.alpha = m_windowData.alpha.value().alpha, .overridden = true}, priority);
else if (opacityIDX == 2)
m_windowData.alphaInactive = CWindowOverridableVar(SAlphaValue{.alpha = m_windowData.alphaInactive.value().alpha, .overridden = true}, priority);
else if (opacityIDX == 3)
m_windowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{.alpha = m_windowData.alphaFullscreen.value().alpha, .overridden = true}, priority);
} else {
if (opacityIDX == 0) {
m_windowData.alpha = CWindowOverridableVar(SAlphaValue{.alpha = std::stof(r), .overridden = false}, priority);
} else if (opacityIDX == 1) {
m_windowData.alphaInactive = CWindowOverridableVar(SAlphaValue{.alpha = std::stof(r), .overridden = false}, priority);
} else if (opacityIDX == 2) {
m_windowData.alphaFullscreen = CWindowOverridableVar(SAlphaValue{.alpha = std::stof(r), .overridden = false}, priority);
} else {
throw std::runtime_error("more than 3 alpha values");
}
opacityIDX++;
}
}
if (opacityIDX == 1) {
m_windowData.alphaInactive = m_windowData.alpha;
m_windowData.alphaFullscreen = m_windowData.alpha;
}
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", r->m_rule, e.what()); }
break;
}
case CWindowRule::RULE_ANIMATION: {
auto STYLE = r->m_rule.substr(r->m_rule.find_first_of(' ') + 1);
m_windowData.animationStyle = CWindowOverridableVar(STYLE, priority);
break;
}
case CWindowRule::RULE_BORDERCOLOR: {
try {
// Each vector will only get used if it has at least one color
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(trim(r->m_rule.substr(r->m_rule.find_first_of(' ') + 1)), 0, 's', true);
// Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_windowData.activeBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), priority);
m_windowData.inactiveBorderColor = CWindowOverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), priority);
return;
}
for (auto const& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) {
activeBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
active = false;
} else if (token.contains("deg"))
inactiveBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
else if (active)
activeBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0));
else
inactiveBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0));
}
activeBorderGradient.updateColorsOk();
// Includes sanity checks for the number of colors in each gradient
if (activeBorderGradient.m_colors.size() > 10 || inactiveBorderGradient.m_colors.size() > 10)
Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", r->m_rule);
else if (activeBorderGradient.m_colors.empty())
Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", r->m_rule);
else if (inactiveBorderGradient.m_colors.empty())
m_windowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
else {
m_windowData.activeBorderColor = CWindowOverridableVar(activeBorderGradient, priority);
m_windowData.inactiveBorderColor = CWindowOverridableVar(inactiveBorderGradient, priority);
}
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", r->m_rule, e.what()); }
break;
}
case CWindowRule::RULE_IDLEINHIBIT: {
auto IDLERULE = r->m_rule.substr(r->m_rule.find_first_of(' ') + 1);
if (IDLERULE == "none")
m_idleInhibitMode = IDLEINHIBIT_NONE;
else if (IDLERULE == "always")
m_idleInhibitMode = IDLEINHIBIT_ALWAYS;
else if (IDLERULE == "focus")
m_idleInhibitMode = IDLEINHIBIT_FOCUS;
else if (IDLERULE == "fullscreen")
m_idleInhibitMode = IDLEINHIBIT_FULLSCREEN;
else
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", IDLERULE);
break;
}
case CWindowRule::RULE_MAXSIZE: {
try {
if (!m_isFloating && !sc<bool>(*PCLAMP_TILED))
return;
const auto VEC = configStringToVector2D(r->m_rule.substr(8));
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for maxsize");
return;
}
m_windowData.maxSize = CWindowOverridableVar(VEC, priority);
clampWindowSize(std::nullopt, m_windowData.maxSize.value());
} catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", r->m_rule, e.what()); }
break;
}
case CWindowRule::RULE_MINSIZE: {
try {
if (!m_isFloating && !sc<bool>(*PCLAMP_TILED))
return;
const auto VEC = configStringToVector2D(r->m_rule.substr(8));
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for minsize");
return;
}
m_windowData.minSize = CWindowOverridableVar(VEC, priority);
clampWindowSize(m_windowData.minSize.value(), std::nullopt);
if (m_groupData.pNextWindow.expired())
setHidden(false);
} catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", r->m_rule, e.what()); }
break;
}
case CWindowRule::RULE_RENDERUNFOCUSED: {
m_windowData.renderUnfocused = CWindowOverridableVar(true, priority);
g_pHyprRenderer->addWindowToRenderUnfocused(m_self.lock());
break;
}
case CWindowRule::RULE_PROP: {
const CVarList VARS(r->m_rule, 0, ' ');
if (auto search = NWindowProperties::intWindowProperties.find(VARS[1]); search != NWindowProperties::intWindowProperties.end()) {
try {
*(search->second(m_self.lock())) = CWindowOverridableVar(sc<Hyprlang::INT>(std::stoi(VARS[2])), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
} else if (auto search = NWindowProperties::floatWindowProperties.find(VARS[1]); search != NWindowProperties::floatWindowProperties.end()) {
try {
*(search->second(m_self.lock())) = CWindowOverridableVar(std::stof(VARS[2]), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
} else if (auto search = NWindowProperties::boolWindowProperties.find(VARS[1]); search != NWindowProperties::boolWindowProperties.end()) {
try {
*(search->second(m_self.lock())) = CWindowOverridableVar(VARS[2].empty() ? true : sc<bool>(std::stoi(VARS[2])), priority);
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
}
break;
}
case CWindowRule::RULE_PERSISTENTSIZE: {
m_windowData.persistentSize = CWindowOverridableVar(true, PRIORITY_WINDOW_RULE);
break;
}
case CWindowRule::RULE_NOVRR: {
m_windowData.noVRR = CWindowOverridableVar(true, priority);
break;
}
default: break;
}
}
void CWindow::updateDynamicRules() {
m_windowData.alpha.unset(PRIORITY_WINDOW_RULE);
m_windowData.alphaInactive.unset(PRIORITY_WINDOW_RULE);
m_windowData.alphaFullscreen.unset(PRIORITY_WINDOW_RULE);
unsetWindowData(PRIORITY_WINDOW_RULE);
m_windowData.animationStyle.unset(PRIORITY_WINDOW_RULE);
m_windowData.maxSize.unset(PRIORITY_WINDOW_RULE);
m_windowData.minSize.unset(PRIORITY_WINDOW_RULE);
m_windowData.activeBorderColor.unset(PRIORITY_WINDOW_RULE);
m_windowData.inactiveBorderColor.unset(PRIORITY_WINDOW_RULE);
m_windowData.renderUnfocused.unset(PRIORITY_WINDOW_RULE);
m_windowData.noVRR.unset(PRIORITY_WINDOW_RULE);
m_idleInhibitMode = IDLEINHIBIT_NONE;
m_tags.removeDynamicTags();
m_matchedRules = g_pConfigManager->getMatchingRules(m_self.lock());
for (const auto& r : m_matchedRules) {
applyDynamicRule(r);
}
EMIT_HOOK_EVENT("windowUpdateRules", m_self.lock());
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
}
// check if the point is "hidden" under a rounded corner of the window // check if the point is "hidden" under a rounded corner of the window
// it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize) // it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize)
// otherwise behaviour is undefined // otherwise behaviour is undefined
@ -924,6 +711,8 @@ void CWindow::createGroup() {
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("1,{:x}", rc<uintptr_t>(this))}); g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("1,{:x}", rc<uintptr_t>(this))});
} }
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
} }
void CWindow::destroyGroup() { void CWindow::destroyGroup() {
@ -943,6 +732,7 @@ void CWindow::destroyGroup() {
g_pCompositor->updateAllWindowsAnimatedDecorationValues(); g_pCompositor->updateAllWindowsAnimatedDecorationValues();
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{:x}", rc<uintptr_t>(this))}); g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{:x}", rc<uintptr_t>(this))});
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
return; return;
} }
@ -969,6 +759,7 @@ void CWindow::destroyGroup() {
g_pKeybindManager->m_groupsLocked = true; g_pKeybindManager->m_groupsLocked = true;
for (auto const& w : members) { for (auto const& w : members) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(w); g_pLayoutManager->getCurrentLayout()->onWindowCreated(w);
w->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
w->updateWindowDecos(); w->updateWindowDecos();
} }
g_pKeybindManager->m_groupsLocked = GROUPSLOCKEDPREV; g_pKeybindManager->m_groupsLocked = GROUPSLOCKEDPREV;
@ -982,6 +773,8 @@ void CWindow::destroyGroup() {
if (!addresses.empty()) if (!addresses.empty())
addresses.pop_back(); addresses.pop_back();
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{}", addresses)}); g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{}", addresses)});
} }
@ -1217,16 +1010,16 @@ float CWindow::rounding() {
static auto PROUNDING = CConfigValue<Hyprlang::INT>("decoration:rounding"); static auto PROUNDING = CConfigValue<Hyprlang::INT>("decoration:rounding");
static auto PROUNDINGPOWER = CConfigValue<Hyprlang::FLOAT>("decoration:rounding_power"); static auto PROUNDINGPOWER = CConfigValue<Hyprlang::FLOAT>("decoration:rounding_power");
float roundingPower = m_windowData.roundingPower.valueOr(*PROUNDINGPOWER); float roundingPower = m_ruleApplicator->roundingPower().valueOr(*PROUNDINGPOWER);
float rounding = m_windowData.rounding.valueOr(*PROUNDING) * (roundingPower / 2.0); /* Make perceived roundness consistent. */ float rounding = m_ruleApplicator->rounding().valueOr(*PROUNDING) * (roundingPower / 2.0); /* Make perceived roundness consistent. */
return m_windowData.noRounding.valueOrDefault() ? 0 : rounding; return rounding;
} }
float CWindow::roundingPower() { float CWindow::roundingPower() {
static auto PROUNDINGPOWER = CConfigValue<Hyprlang::FLOAT>("decoration:rounding_power"); static auto PROUNDINGPOWER = CConfigValue<Hyprlang::FLOAT>("decoration:rounding_power");
return m_windowData.roundingPower.valueOr(std::clamp(*PROUNDINGPOWER, 1.F, 10.F)); return m_ruleApplicator->roundingPower().valueOr(std::clamp(*PROUNDINGPOWER, 1.F, 10.F));
} }
void CWindow::updateWindowData() { void CWindow::updateWindowData() {
@ -1236,50 +1029,43 @@ void CWindow::updateWindowData() {
} }
void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) { void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) {
static auto PNOBORDERONFLOATING = CConfigValue<Hyprlang::INT>("general:no_border_on_floating"); m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE);
m_ruleApplicator->decorate().matchOptional(workspaceRule.decorate, Desktop::Types::PRIORITY_WORKSPACE_RULE);
if (*PNOBORDERONFLOATING) m_ruleApplicator->borderSize().matchOptional(workspaceRule.noBorder ? std::optional<Hyprlang::INT>(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
m_windowData.noBorder = CWindowOverridableVar(m_isFloating, PRIORITY_LAYOUT); m_ruleApplicator->rounding().matchOptional(workspaceRule.noRounding.value_or(false) ? std::optional<Hyprlang::INT>(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
else m_ruleApplicator->noShadow().matchOptional(workspaceRule.noShadow, Desktop::Types::PRIORITY_WORKSPACE_RULE);
m_windowData.noBorder.unset(PRIORITY_LAYOUT);
m_windowData.borderSize.matchOptional(workspaceRule.borderSize, PRIORITY_WORKSPACE_RULE);
m_windowData.decorate.matchOptional(workspaceRule.decorate, PRIORITY_WORKSPACE_RULE);
m_windowData.noBorder.matchOptional(workspaceRule.noBorder, PRIORITY_WORKSPACE_RULE);
m_windowData.noRounding.matchOptional(workspaceRule.noRounding, PRIORITY_WORKSPACE_RULE);
m_windowData.noShadow.matchOptional(workspaceRule.noShadow, PRIORITY_WORKSPACE_RULE);
} }
int CWindow::getRealBorderSize() { int CWindow::getRealBorderSize() {
if (m_windowData.noBorder.valueOrDefault() || (m_workspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) || !m_windowData.decorate.valueOrDefault()) if ((m_workspace && isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) || !m_ruleApplicator->decorate().valueOrDefault())
return 0; return 0;
static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size"); static auto PBORDERSIZE = CConfigValue<Hyprlang::INT>("general:border_size");
return m_windowData.borderSize.valueOr(*PBORDERSIZE); return m_ruleApplicator->borderSize().valueOr(*PBORDERSIZE);
} }
float CWindow::getScrollMouse() { float CWindow::getScrollMouse() {
static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor"); static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor");
return m_windowData.scrollMouse.valueOr(*PINPUTSCROLLFACTOR); return m_ruleApplicator->scrollMouse().valueOr(*PINPUTSCROLLFACTOR);
} }
float CWindow::getScrollTouchpad() { float CWindow::getScrollTouchpad() {
static auto PTOUCHPADSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:touchpad:scroll_factor"); static auto PTOUCHPADSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:touchpad:scroll_factor");
return m_windowData.scrollTouchpad.valueOr(*PTOUCHPADSCROLLFACTOR); return m_ruleApplicator->scrollTouchpad().valueOr(*PTOUCHPADSCROLLFACTOR);
} }
bool CWindow::isScrollMouseOverridden() { bool CWindow::isScrollMouseOverridden() {
return m_windowData.scrollMouse.hasValue(); return m_ruleApplicator->scrollMouse().hasValue();
} }
bool CWindow::isScrollTouchpadOverridden() { bool CWindow::isScrollTouchpadOverridden() {
return m_windowData.scrollTouchpad.hasValue(); return m_ruleApplicator->scrollTouchpad().hasValue();
} }
bool CWindow::canBeTorn() { bool CWindow::canBeTorn() {
static auto PTEARING = CConfigValue<Hyprlang::INT>("general:allow_tearing"); static auto PTEARING = CConfigValue<Hyprlang::INT>("general:allow_tearing");
return m_windowData.tearing.valueOr(m_tearingHint) && *PTEARING; return m_ruleApplicator->tearing().valueOr(m_tearingHint) && *PTEARING;
} }
void CWindow::setSuspended(bool suspend) { void CWindow::setSuspended(bool suspend) {
@ -1454,7 +1240,8 @@ void CWindow::activate(bool force) {
g_pEventManager->postEvent(SHyprIPCEvent{.event = "urgent", .data = std::format("{:x}", rc<uintptr_t>(this))}); g_pEventManager->postEvent(SHyprIPCEvent{.event = "urgent", .data = std::format("{:x}", rc<uintptr_t>(this))});
EMIT_HOOK_EVENT("urgent", m_self.lock()); EMIT_HOOK_EVENT("urgent", m_self.lock());
if (!force && (!m_windowData.focusOnActivate.valueOr(*PFOCUSONACTIVATE) || (m_suppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_suppressedEvents & SUPPRESS_ACTIVATE))) if (!force &&
(!m_ruleApplicator->focusOnActivate().valueOr(*PFOCUSONACTIVATE) || (m_suppressedEvents & SUPPRESS_ACTIVATE_FOCUSONLY) || (m_suppressedEvents & SUPPRESS_ACTIVATE)))
return; return;
if (!m_isMapped) { if (!m_isMapped) {
@ -1539,8 +1326,7 @@ void CWindow::onUpdateMeta() {
} }
if (doUpdate) { if (doUpdate) {
updateDynamicRules(); m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TITLE | Desktop::Rule::RULE_PROP_CLASS);
g_pCompositor->updateWindowAnimatedDecorationValues(m_self.lock());
updateToplevel(); updateToplevel();
} }
} }
@ -1718,18 +1504,6 @@ PHLWINDOW CWindow::getSwallower() {
return candidates[0]; return candidates[0];
} }
void CWindow::unsetWindowData(eOverridePriority priority) {
for (auto const& element : NWindowProperties::boolWindowProperties) {
element.second(m_self.lock())->unset(priority);
}
for (auto const& element : NWindowProperties::intWindowProperties) {
element.second(m_self.lock())->unset(priority);
}
for (auto const& element : NWindowProperties::floatWindowProperties) {
element.second(m_self.lock())->unset(priority);
}
}
bool CWindow::isX11OverrideRedirect() { bool CWindow::isX11OverrideRedirect() {
return m_xwaylandSurface && m_xwaylandSurface->m_overrideRedirect; return m_xwaylandSurface && m_xwaylandSurface->m_overrideRedirect;
} }
@ -1753,7 +1527,7 @@ Vector2D CWindow::requestedMinSize() {
Vector2D CWindow::requestedMaxSize() { Vector2D CWindow::requestedMaxSize() {
constexpr int NO_MAX_SIZE_LIMIT = 99999; constexpr int NO_MAX_SIZE_LIMIT = 99999;
if (((m_isX11 && !m_xwaylandSurface->m_sizeHints) || (!m_isX11 && (!m_xdgSurface || !m_xdgSurface->m_toplevel)) || m_windowData.noMaxSize.valueOrDefault())) if (((m_isX11 && !m_xwaylandSurface->m_sizeHints) || (!m_isX11 && (!m_xdgSurface || !m_xdgSurface->m_toplevel)) || m_ruleApplicator->noMaxSize().valueOrDefault()))
return Vector2D(NO_MAX_SIZE_LIMIT, NO_MAX_SIZE_LIMIT); return Vector2D(NO_MAX_SIZE_LIMIT, NO_MAX_SIZE_LIMIT);
Vector2D maxSize = m_isX11 ? Vector2D(m_xwaylandSurface->m_sizeHints->max_width, m_xwaylandSurface->m_sizeHints->max_height) : m_xdgSurface->m_toplevel->layoutMaxSize(); Vector2D maxSize = m_isX11 ? Vector2D(m_xwaylandSurface->m_sizeHints->max_width, m_xwaylandSurface->m_sizeHints->max_height) : m_xdgSurface->m_toplevel->layoutMaxSize();
@ -1951,3 +1725,127 @@ Vector2D CWindow::getReportedSize() {
return m_wlSurface->resource()->m_current.ackedSize; return m_wlSurface->resource()->m_current.ackedSize;
return m_reportedSize; return m_reportedSize;
} }
void CWindow::updateDecorationValues() {
static auto PACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.active_border");
static auto PINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.inactive_border");
static auto PNOGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border_active");
static auto PNOGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("general:col.nogroup_border");
static auto PGROUPACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_active");
static auto PGROUPINACTIVECOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_inactive");
static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
static auto PINACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:inactive_opacity");
static auto PACTIVEALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:active_opacity");
static auto PFULLSCREENALPHA = CConfigValue<Hyprlang::FLOAT>("decoration:fullscreen_opacity");
static auto PSHADOWCOL = CConfigValue<Hyprlang::INT>("decoration:shadow:color");
static auto PSHADOWCOLINACTIVE = CConfigValue<Hyprlang::INT>("decoration:shadow:color_inactive");
static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
static auto PDIMENABLED = CConfigValue<Hyprlang::INT>("decoration:dim_inactive");
static auto PDIMMODAL = CConfigValue<Hyprlang::INT>("decoration:dim_modal");
auto* const ACTIVECOL = sc<CGradientValueData*>((PACTIVECOL.ptr())->getData());
auto* const INACTIVECOL = sc<CGradientValueData*>((PINACTIVECOL.ptr())->getData());
auto* const NOGROUPACTIVECOL = sc<CGradientValueData*>((PNOGROUPACTIVECOL.ptr())->getData());
auto* const NOGROUPINACTIVECOL = sc<CGradientValueData*>((PNOGROUPINACTIVECOL.ptr())->getData());
auto* const GROUPACTIVECOL = sc<CGradientValueData*>((PGROUPACTIVECOL.ptr())->getData());
auto* const GROUPINACTIVECOL = sc<CGradientValueData*>((PGROUPINACTIVECOL.ptr())->getData());
auto* const GROUPACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPACTIVELOCKEDCOL.ptr())->getData());
auto* const GROUPINACTIVELOCKEDCOL = sc<CGradientValueData*>((PGROUPINACTIVELOCKEDCOL.ptr())->getData());
auto setBorderColor = [&](CGradientValueData grad) -> void {
if (grad == m_realBorderColor)
return;
m_realBorderColorPrevious = m_realBorderColor;
m_realBorderColor = grad;
m_borderFadeAnimationProgress->setValueAndWarp(0.f);
*m_borderFadeAnimationProgress = 1.f;
};
const bool IS_SHADOWED_BY_MODAL = m_xdgSurface && m_xdgSurface->m_toplevel && m_xdgSurface->m_toplevel->anyChildModal();
// border
const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(m_self.lock());
if (RENDERDATA.isBorderGradient)
setBorderColor(*RENDERDATA.borderGradient);
else {
const bool GROUPLOCKED = m_groupData.pNextWindow.lock() ? getGroupHead()->m_groupData.locked : false;
if (m_self == g_pCompositor->m_lastWindow) {
const auto* const ACTIVECOLOR =
!m_groupData.pNextWindow.lock() ? (!m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
setBorderColor(m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR));
} else {
const auto* const INACTIVECOLOR =
!m_groupData.pNextWindow.lock() ? (!m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
setBorderColor(m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR));
}
}
// opacity
const auto PWORKSPACE = m_workspace;
if (isEffectiveInternalFSMode(FSMODE_FULLSCREEN)) {
*m_activeInactiveAlpha = m_ruleApplicator->alphaFullscreen().valueOrDefault().applyAlpha(*PFULLSCREENALPHA);
} else {
if (m_self == g_pCompositor->m_lastWindow)
*m_activeInactiveAlpha = m_ruleApplicator->alpha().valueOrDefault().applyAlpha(*PACTIVEALPHA);
else
*m_activeInactiveAlpha = m_ruleApplicator->alphaInactive().valueOrDefault().applyAlpha(*PINACTIVEALPHA);
}
// dim
float goalDim = 1.F;
if (m_self == g_pCompositor->m_lastWindow.lock() || m_ruleApplicator->noDim().valueOrDefault() || !*PDIMENABLED)
goalDim = 0;
else
goalDim = *PDIMSTRENGTH;
if (IS_SHADOWED_BY_MODAL && *PDIMMODAL)
goalDim += (1.F - goalDim) / 2.F;
*m_dimPercent = goalDim;
// shadow
if (!isX11OverrideRedirect() && !m_X11DoesntWantBorders) {
if (m_self == g_pCompositor->m_lastWindow)
*m_realShadowColor = CHyprColor(*PSHADOWCOL);
else
*m_realShadowColor = CHyprColor(*PSHADOWCOLINACTIVE != -1 ? *PSHADOWCOLINACTIVE : *PSHADOWCOL);
} else
m_realShadowColor->setValueAndWarp(CHyprColor(0, 0, 0, 0)); // no shadow
updateWindowDecos();
}
std::optional<double> CWindow::calculateSingleExpr(const std::string& s) {
const auto PMONITOR = m_monitor ? m_monitor : g_pCompositor->m_lastMonitor;
const auto CURSOR_LOCAL = g_pInputManager->getMouseCoordsInternal() - (PMONITOR ? PMONITOR->m_position : Vector2D{});
Math::CExpression expr;
expr.addVariable("window_w", m_realSize->goal().x);
expr.addVariable("window_h", m_realSize->goal().y);
expr.addVariable("window_x", m_realPosition->goal().x - (PMONITOR ? PMONITOR->m_position.x : 0));
expr.addVariable("window_y", m_realPosition->goal().y - (PMONITOR ? PMONITOR->m_position.y : 0));
expr.addVariable("monitor_w", PMONITOR ? PMONITOR->m_size.x : 1920);
expr.addVariable("monitor_h", PMONITOR ? PMONITOR->m_size.y : 1080);
expr.addVariable("cursor_x", CURSOR_LOCAL.x);
expr.addVariable("cursor_y", CURSOR_LOCAL.y);
return expr.compute(s);
}
std::optional<Vector2D> CWindow::calculateExpression(const std::string& s) {
auto spacePos = s.find(' ');
if (spacePos == std::string::npos)
return std::nullopt;
const auto LHS = calculateSingleExpr(s.substr(0, spacePos));
const auto RHS = calculateSingleExpr(s.substr(spacePos + 1));
if (!LHS || !RHS)
return std::nullopt;
return Vector2D{*LHS, *RHS};
}

View file

@ -16,20 +16,12 @@
#include "Subsurface.hpp" #include "Subsurface.hpp"
#include "WLSurface.hpp" #include "WLSurface.hpp"
#include "Workspace.hpp" #include "Workspace.hpp"
#include "WindowRule.hpp" #include "rule/windowRule/WindowRuleApplicator.hpp"
#include "WindowOverridableVar.hpp"
#include "../protocols/types/ContentType.hpp" #include "../protocols/types/ContentType.hpp"
class CXDGSurfaceResource; class CXDGSurfaceResource;
class CXWaylandSurface; class CXWaylandSurface;
enum eIdleInhibitMode : uint8_t {
IDLEINHIBIT_NONE = 0,
IDLEINHIBIT_ALWAYS,
IDLEINHIBIT_FULLSCREEN,
IDLEINHIBIT_FOCUS
};
enum eGroupRules : uint8_t { enum eGroupRules : uint8_t {
// effective only during first map, except for _ALWAYS variant // effective only during first map, except for _ALWAYS variant
GROUP_NONE = 0, GROUP_NONE = 0,
@ -65,65 +57,6 @@ enum eSuppressEvents : uint8_t {
class IWindowTransformer; class IWindowTransformer;
struct SAlphaValue {
float alpha;
bool overridden;
float applyAlpha(float a) const {
if (overridden)
return alpha;
else
return alpha * a;
};
};
struct SWindowData {
CWindowOverridableVar<SAlphaValue> alpha = SAlphaValue{.alpha = 1.f, .overridden = false};
CWindowOverridableVar<SAlphaValue> alphaInactive = SAlphaValue{.alpha = 1.f, .overridden = false};
CWindowOverridableVar<SAlphaValue> alphaFullscreen = SAlphaValue{.alpha = 1.f, .overridden = false};
CWindowOverridableVar<bool> allowsInput = false;
CWindowOverridableVar<bool> dimAround = false;
CWindowOverridableVar<bool> decorate = true;
CWindowOverridableVar<bool> focusOnActivate = false;
CWindowOverridableVar<bool> keepAspectRatio = false;
CWindowOverridableVar<bool> nearestNeighbor = false;
CWindowOverridableVar<bool> noAnim = false;
CWindowOverridableVar<bool> noBorder = false;
CWindowOverridableVar<bool> noBlur = false;
CWindowOverridableVar<bool> noDim = false;
CWindowOverridableVar<bool> noFocus = false;
CWindowOverridableVar<bool> noMaxSize = false;
CWindowOverridableVar<bool> noRounding = false;
CWindowOverridableVar<bool> noShadow = false;
CWindowOverridableVar<bool> noShortcutsInhibit = false;
CWindowOverridableVar<bool> opaque = false;
CWindowOverridableVar<bool> RGBX = false;
CWindowOverridableVar<bool> syncFullscreen = true;
CWindowOverridableVar<bool> tearing = false;
CWindowOverridableVar<bool> xray = false;
CWindowOverridableVar<bool> renderUnfocused = false;
CWindowOverridableVar<bool> noFollowMouse = false;
CWindowOverridableVar<bool> noScreenShare = false;
CWindowOverridableVar<bool> noVRR = false;
CWindowOverridableVar<Hyprlang::INT> borderSize = {std::string("general:border_size"), sc<Hyprlang::INT>(0), std::nullopt};
CWindowOverridableVar<Hyprlang::INT> rounding = {std::string("decoration:rounding"), sc<Hyprlang::INT>(0), std::nullopt};
CWindowOverridableVar<Hyprlang::FLOAT> roundingPower = {std::string("decoration:rounding_power")};
CWindowOverridableVar<Hyprlang::FLOAT> scrollMouse = {std::string("input:scroll_factor")};
CWindowOverridableVar<Hyprlang::FLOAT> scrollTouchpad = {std::string("input:touchpad:scroll_factor")};
CWindowOverridableVar<std::string> animationStyle;
CWindowOverridableVar<Vector2D> maxSize;
CWindowOverridableVar<Vector2D> minSize;
CWindowOverridableVar<CGradientValueData> activeBorderColor;
CWindowOverridableVar<CGradientValueData> inactiveBorderColor;
CWindowOverridableVar<bool> persistentSize;
};
struct SInitialWorkspaceToken { struct SInitialWorkspaceToken {
PHLWINDOWREF primaryOwner; PHLWINDOWREF primaryOwner;
std::string workspace; std::string workspace;
@ -256,7 +189,7 @@ class CWindow {
std::vector<IHyprWindowDecoration*> m_decosToRemove; std::vector<IHyprWindowDecoration*> m_decosToRemove;
// Special render data, rules, etc // Special render data, rules, etc
SWindowData m_windowData; UP<Desktop::Rule::CWindowRuleApplicator> m_ruleApplicator;
// Transformers // Transformers
std::vector<UP<IWindowTransformer>> m_transformers; std::vector<UP<IWindowTransformer>> m_transformers;
@ -280,15 +213,9 @@ class CWindow {
bool m_currentlySwallowed = false; bool m_currentlySwallowed = false;
bool m_groupSwallowed = false; bool m_groupSwallowed = false;
// focus stuff
bool m_stayFocused = false;
// for toplevel monitor events // for toplevel monitor events
MONITORID m_lastSurfaceMonitorID = -1; MONITORID m_lastSurfaceMonitorID = -1;
// for idle inhibiting windows
eIdleInhibitMode m_idleInhibitMode = IDLEINHIBIT_NONE;
// initial token. Will be unregistered on workspace change or timeout of 2 minutes // initial token. Will be unregistered on workspace change or timeout of 2 minutes
std::string m_initialWorkspaceToken = ""; std::string m_initialWorkspaceToken = "";
@ -303,12 +230,6 @@ class CWindow {
bool m_tearingHint = false; bool m_tearingHint = false;
// stores the currently matched window rules
std::vector<SP<CWindowRule>> m_matchedRules;
// window tags
CTagKeeper m_tags;
// ANR // ANR
PHLANIMVAR<float> m_notRespondingTint; PHLANIMVAR<float> m_notRespondingTint;
@ -342,8 +263,7 @@ class CWindow {
void onMap(); void onMap();
void setHidden(bool hidden); void setHidden(bool hidden);
bool isHidden(); bool isHidden();
void applyDynamicRule(const SP<CWindowRule>& r); void updateDecorationValues();
void updateDynamicRules();
SBoxExtents getFullWindowReservedArea(); SBoxExtents getFullWindowReservedArea();
Vector2D middle(); Vector2D middle();
bool opaque(); bool opaque();
@ -397,7 +317,6 @@ class CWindow {
std::string fetchClass(); std::string fetchClass();
void warpCursor(bool force = false); void warpCursor(bool force = false);
PHLWINDOW getSwallower(); PHLWINDOW getSwallower();
void unsetWindowData(eOverridePriority priority);
bool isX11OverrideRedirect(); bool isX11OverrideRedirect();
bool isModal(); bool isModal();
Vector2D requestedMinSize(); Vector2D requestedMinSize();
@ -418,6 +337,7 @@ class CWindow {
bool priorityFocus(); bool priorityFocus();
SP<CWLSurfaceResource> getSolitaryResource(); SP<CWLSurfaceResource> getSolitaryResource();
Vector2D getReportedSize(); Vector2D getReportedSize();
std::optional<Vector2D> calculateExpression(const std::string& s);
CBox getWindowMainSurfaceBox() const { CBox getWindowMainSurfaceBox() const {
return {m_realPosition->value().x, m_realPosition->value().y, m_realSize->value().x, m_realSize->value().y}; return {m_realPosition->value().x, m_realPosition->value().y, m_realSize->value().x, m_realSize->value().y};
@ -448,6 +368,8 @@ class CWindow {
} m_listeners; } m_listeners;
private: private:
std::optional<double> calculateSingleExpr(const std::string& s);
// For hidden windows and stuff // For hidden windows and stuff
bool m_hidden = false; bool m_hidden = false;
bool m_suspended = false; bool m_suspended = false;
@ -474,45 +396,6 @@ inline bool validMapped(PHLWINDOWREF w) {
return w->m_isMapped; return w->m_isMapped;
} }
namespace NWindowProperties {
static const std::unordered_map<std::string, std::function<CWindowOverridableVar<bool>*(const PHLWINDOW&)>> boolWindowProperties = {
{"allowsinput", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.allowsInput; }},
{"dimaround", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.dimAround; }},
{"decorate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.decorate; }},
{"focusonactivate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.focusOnActivate; }},
{"keepaspectratio", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.keepAspectRatio; }},
{"nearestneighbor", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.nearestNeighbor; }},
{"noanim", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noAnim; }},
{"noblur", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noBlur; }},
{"noborder", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noBorder; }},
{"nodim", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noDim; }},
{"nofocus", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noFocus; }},
{"nomaxsize", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noMaxSize; }},
{"norounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noRounding; }},
{"noshadow", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noShadow; }},
{"noshortcutsinhibit", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noShortcutsInhibit; }},
{"opaque", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.opaque; }},
{"forcergbx", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.RGBX; }},
{"syncfullscreen", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.syncFullscreen; }},
{"novrr", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noVRR; }},
{"immediate", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.tearing; }},
{"xray", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.xray; }},
{"nofollowmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noFollowMouse; }},
{"noscreenshare", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.noScreenShare; }},
};
const std::unordered_map<std::string, std::function<CWindowOverridableVar<Hyprlang::INT>*(const PHLWINDOW&)>> intWindowProperties = {
{"rounding", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.rounding; }},
{"bordersize", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.borderSize; }},
};
const std::unordered_map<std::string, std::function<CWindowOverridableVar<Hyprlang::FLOAT>*(PHLWINDOW)>> floatWindowProperties = {
{"roundingpower", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.roundingPower; }},
{"scrollmouse", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.scrollMouse; }},
{"scrolltouchpad", [](const PHLWINDOW& pWindow) { return &pWindow->m_windowData.scrollTouchpad; }},
};
};
/** /**
format specification format specification
- 'x', only address, equivalent of (uintpr_t)CWindow* - 'x', only address, equivalent of (uintpr_t)CWindow*

View file

@ -1,132 +0,0 @@
#pragma once
#include <cstdint>
#include <type_traits>
#include <any>
#include "../config/ConfigValue.hpp"
enum eOverridePriority : uint8_t {
PRIORITY_LAYOUT = 0,
PRIORITY_WORKSPACE_RULE,
PRIORITY_WINDOW_RULE,
PRIORITY_SET_PROP,
};
template <typename T>
T clampOptional(T const& value, std::optional<T> const& min, std::optional<T> const& max) {
return std::clamp(value, min.value_or(std::numeric_limits<T>::min()), max.value_or(std::numeric_limits<T>::max()));
}
template <typename T, bool Extended = std::is_same_v<T, bool> || std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, Hyprlang::FLOAT>>
class CWindowOverridableVar {
public:
CWindowOverridableVar(T const& value, eOverridePriority priority) {
m_values[priority] = value;
}
CWindowOverridableVar(T const& value) : m_defaultValue{value} {}
CWindowOverridableVar(T const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {}
CWindowOverridableVar(std::string const& value)
requires(Extended && !std::is_same_v<T, bool>)
: m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
CWindowOverridableVar(std::string const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt)
requires(Extended && !std::is_same_v<T, bool>)
: m_minValue(min), m_maxValue(max), m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
CWindowOverridableVar() = default;
~CWindowOverridableVar() = default;
CWindowOverridableVar& operator=(CWindowOverridableVar<T> const& other) {
// Self-assignment check
if (this == &other)
return *this;
for (auto const& value : other.m_values) {
if constexpr (Extended && !std::is_same_v<T, bool>)
m_values[value.first] = clampOptional(value.second, m_minValue, m_maxValue);
else
m_values[value.first] = value.second;
}
return *this;
}
void unset(eOverridePriority priority) {
m_values.erase(priority);
}
bool hasValue() {
return !m_values.empty();
}
T value() {
if (!m_values.empty())
return std::prev(m_values.end())->second;
else
throw std::bad_optional_access();
}
T valueOr(T const& other) {
if (hasValue())
return value();
else
return other;
}
T valueOrDefault()
requires(Extended && !std::is_same_v<T, bool>)
{
if (hasValue())
return value();
else if (m_defaultValue.has_value())
return m_defaultValue.value();
else
return **std::any_cast<SP<CConfigValue<T>>>(m_configValue);
}
T valueOrDefault()
requires(!Extended || std::is_same_v<T, bool>)
{
if (hasValue())
return value();
else if (!m_defaultValue.has_value())
throw std::bad_optional_access();
else
return m_defaultValue.value();
}
eOverridePriority getPriority() {
if (!m_values.empty())
return std::prev(m_values.end())->first;
else
throw std::bad_optional_access();
}
void increment(T const& other, eOverridePriority priority) {
if constexpr (std::is_same_v<T, bool>)
m_values[priority] = valueOr(false) ^ other;
else
m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue);
}
void matchOptional(std::optional<T> const& optValue, eOverridePriority priority) {
if (optValue.has_value())
m_values[priority] = optValue.value();
else
unset(priority);
}
operator std::optional<T>() {
if (hasValue())
return value();
else
return std::nullopt;
}
private:
std::map<eOverridePriority, T> m_values;
std::optional<T> m_defaultValue; // used for toggling, so required for bool
std::optional<T> m_minValue;
std::optional<T> m_maxValue;
std::any m_configValue; // only there for select variables
};

View file

@ -1,99 +0,0 @@
#include "WindowRule.hpp"
#include <unordered_set>
#include <algorithm>
#include <re2/re2.h>
#include "../config/ConfigManager.hpp"
static const auto RULES = std::unordered_set<std::string>{
"float", "fullscreen", "maximize", "noinitialfocus", "pin", "stayfocused", "tile", "renderunfocused", "persistentsize",
};
static const auto RULES_PREFIX = std::unordered_set<std::string>{
"animation", "bordercolor", "bordersize", "center", "content", "fullscreenstate", "group", "idleinhibit", "maxsize", "minsize", "monitor",
"move", "noclosefor", "opacity", "plugin:", "prop", "pseudo", "rounding", "roundingpower", "scrollmouse", "scrolltouchpad", "size",
"suppressevent", "tag", "workspace", "xray", "novrr",
};
CWindowRule::CWindowRule(const std::string& rule, const std::string& value, bool isV2, bool isExecRule) : m_value(value), m_rule(rule), m_v2(isV2), m_execRule(isExecRule) {
const auto VALS = CVarList(rule, 2, ' ');
const bool VALID = RULES.contains(rule) || std::ranges::any_of(RULES_PREFIX, [&rule](auto prefix) { return rule.starts_with(prefix); }) ||
(NWindowProperties::boolWindowProperties.contains(VALS[0])) || (NWindowProperties::intWindowProperties.contains(VALS[0])) ||
(NWindowProperties::floatWindowProperties.contains(VALS[0]));
if (!VALID)
return;
if (rule == "float")
m_ruleType = RULE_FLOAT;
else if (rule == "fullscreen")
m_ruleType = RULE_FULLSCREEN;
else if (rule == "maximize")
m_ruleType = RULE_MAXIMIZE;
else if (rule == "noinitialfocus")
m_ruleType = RULE_NOINITIALFOCUS;
else if (rule == "pin")
m_ruleType = RULE_PIN;
else if (rule == "stayfocused")
m_ruleType = RULE_STAYFOCUSED;
else if (rule == "tile")
m_ruleType = RULE_TILE;
else if (rule == "renderunfocused")
m_ruleType = RULE_RENDERUNFOCUSED;
else if (rule == "persistentsize")
m_ruleType = RULE_PERSISTENTSIZE;
else if (rule.starts_with("animation"))
m_ruleType = RULE_ANIMATION;
else if (rule.starts_with("bordercolor"))
m_ruleType = RULE_BORDERCOLOR;
else if (rule.starts_with("center"))
m_ruleType = RULE_CENTER;
else if (rule.starts_with("fullscreenstate"))
m_ruleType = RULE_FULLSCREENSTATE;
else if (rule.starts_with("group"))
m_ruleType = RULE_GROUP;
else if (rule.starts_with("idleinhibit"))
m_ruleType = RULE_IDLEINHIBIT;
else if (rule.starts_with("maxsize"))
m_ruleType = RULE_MAXSIZE;
else if (rule.starts_with("minsize"))
m_ruleType = RULE_MINSIZE;
else if (rule.starts_with("monitor"))
m_ruleType = RULE_MONITOR;
else if (rule.starts_with("move"))
m_ruleType = RULE_MOVE;
else if (rule.starts_with("opacity"))
m_ruleType = RULE_OPACITY;
else if (rule.starts_with("plugin:"))
m_ruleType = RULE_PLUGIN;
else if (rule.starts_with("pseudo"))
m_ruleType = RULE_PSEUDO;
else if (rule.starts_with("size"))
m_ruleType = RULE_SIZE;
else if (rule.starts_with("suppressevent"))
m_ruleType = RULE_SUPPRESSEVENT;
else if (rule.starts_with("novrr"))
m_ruleType = RULE_NOVRR;
else if (rule.starts_with("tag"))
m_ruleType = RULE_TAG;
else if (rule.starts_with("workspace"))
m_ruleType = RULE_WORKSPACE;
else if (rule.starts_with("prop"))
m_ruleType = RULE_PROP;
else if (rule.starts_with("content"))
m_ruleType = RULE_CONTENT;
else if (rule.starts_with("noclosefor"))
m_ruleType = RULE_NOCLOSEFOR;
else {
// check if this is a prop.
const CVarList VARS(rule, 0, 's', true);
const bool ISPROP = NWindowProperties::intWindowProperties.contains(VARS[0]) || NWindowProperties::boolWindowProperties.contains(VARS[0]) ||
NWindowProperties::floatWindowProperties.contains(VARS[0]);
if (ISPROP) {
*const_cast<std::string*>(&m_rule) = "prop " + rule;
m_ruleType = RULE_PROP;
Debug::log(LOG, "CWindowRule: direct prop rule found, rewritten {} -> {}", rule, m_rule);
} else {
Debug::log(ERR, "CWindowRule: didn't match a rule that was found valid?!");
m_ruleType = RULE_INVALID;
}
}
}

View file

@ -1,76 +0,0 @@
#pragma once
#include <string>
#include <cstdint>
#include "Rule.hpp"
class CWindowRule {
public:
CWindowRule(const std::string& rule, const std::string& value, bool isV2 = false, bool isExecRule = false);
enum eRuleType : uint8_t {
RULE_INVALID = 0,
RULE_FLOAT,
RULE_FULLSCREEN,
RULE_MAXIMIZE,
RULE_NOINITIALFOCUS,
RULE_PIN,
RULE_STAYFOCUSED,
RULE_TILE,
RULE_RENDERUNFOCUSED,
RULE_ANIMATION,
RULE_BORDERCOLOR,
RULE_CENTER,
RULE_FULLSCREENSTATE,
RULE_GROUP,
RULE_IDLEINHIBIT,
RULE_MAXSIZE,
RULE_MINSIZE,
RULE_MONITOR,
RULE_MOVE,
RULE_OPACITY,
RULE_PLUGIN,
RULE_PSEUDO,
RULE_SIZE,
RULE_SUPPRESSEVENT,
RULE_TAG,
RULE_WORKSPACE,
RULE_PROP,
RULE_CONTENT,
RULE_PERSISTENTSIZE,
RULE_NOCLOSEFOR,
RULE_NOVRR,
};
eRuleType m_ruleType = RULE_INVALID;
const std::string m_value;
const std::string m_rule;
const bool m_v2 = false;
const bool m_execRule = false;
std::string m_title;
std::string m_class;
std::string m_initialTitle;
std::string m_initialClass;
std::string m_tag;
int m_X11 = -1; // -1 means "ANY"
int m_floating = -1;
int m_fullscreen = -1;
int m_pinned = -1;
int m_focus = -1;
int m_group = -1;
int m_modal = -1;
std::string m_fullscreenState = ""; // empty means any
std::string m_onWorkspace = ""; // empty means any
std::string m_workspace = ""; // empty means any
std::string m_contentType = ""; // empty means any
std::string m_xdgTag = ""; // empty means any
// precompiled regexes
CRuleRegexContainer m_titleRegex;
CRuleRegexContainer m_classRegex;
CRuleRegexContainer m_initialTitleRegex;
CRuleRegexContainer m_initialClassRegex;
CRuleRegexContainer m_v1Regex;
};

View file

@ -542,7 +542,7 @@ void CWorkspace::updateWindows() {
if (!w->m_isMapped || w->m_workspace != m_self) if (!w->m_isMapped || w->m_workspace != m_self)
continue; continue;
w->updateDynamicRules(); w->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
} }
} }

View file

@ -0,0 +1,56 @@
#include "Engine.hpp"
#include "Rule.hpp"
#include "../LayerSurface.hpp"
#include "../../Compositor.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
SP<CRuleEngine> Rule::ruleEngine() {
static SP<CRuleEngine> engine = makeShared<CRuleEngine>();
return engine;
}
void CRuleEngine::registerRule(SP<IRule>&& rule) {
m_rules.emplace_back(std::move(rule));
}
void CRuleEngine::unregisterRule(const std::string& name) {
if (name.empty())
return;
std::erase_if(m_rules, [&name](const auto& el) { return el->name() == name; });
}
void CRuleEngine::unregisterRule(const SP<IRule>& rule) {
std::erase(m_rules, rule);
cleanExecRules();
}
void CRuleEngine::cleanExecRules() {
std::erase_if(m_rules, [](const auto& e) { return e->isExecRule() && e->execExpired(); });
}
void CRuleEngine::updateAllRules() {
cleanExecRules();
for (const auto& w : g_pCompositor->m_windows) {
if (!validMapped(w) || w->isHidden())
continue;
w->m_ruleApplicator->propertiesChanged(RULE_PROP_ALL);
}
for (const auto& ls : g_pCompositor->m_layers) {
if (!validMapped(ls))
continue;
ls->m_ruleApplicator->propertiesChanged(RULE_PROP_ALL);
}
}
void CRuleEngine::clearAllRules() {
std::erase_if(m_rules, [](const auto& e) { return !e->isExecRule() || e->execExpired(); });
}
const std::vector<SP<IRule>>& CRuleEngine::rules() {
return m_rules;
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "Rule.hpp"
namespace Desktop::Rule {
class CRuleEngine {
public:
CRuleEngine() = default;
~CRuleEngine() = default;
void registerRule(SP<IRule>&& rule);
void unregisterRule(const std::string& name);
void unregisterRule(const SP<IRule>& rule);
void updateAllRules();
void cleanExecRules();
void clearAllRules();
const std::vector<SP<IRule>>& rules();
private:
std::vector<SP<IRule>> m_rules;
};
SP<CRuleEngine> ruleEngine();
}

149
src/desktop/rule/Rule.cpp Normal file
View file

@ -0,0 +1,149 @@
#include "Rule.hpp"
#include "../../debug/Log.hpp"
#include <re2/re2.h>
#include "matchEngine/RegexMatchEngine.hpp"
#include "matchEngine/BoolMatchEngine.hpp"
#include "matchEngine/IntMatchEngine.hpp"
#include "matchEngine/WorkspaceMatchEngine.hpp"
#include "matchEngine/TagMatchEngine.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
static const std::unordered_map<eRuleProperty, std::string> MATCH_PROP_STRINGS = {
{RULE_PROP_CLASS, "class"}, //
{RULE_PROP_TITLE, "title"}, //
{RULE_PROP_INITIAL_CLASS, "initial_class"}, //
{RULE_PROP_INITIAL_TITLE, "initial_title"}, //
{RULE_PROP_FLOATING, "float"}, //
{RULE_PROP_TAG, "tag"}, //
{RULE_PROP_XWAYLAND, "xwayland"}, //
{RULE_PROP_FULLSCREEN, "fullscreen"}, //
{RULE_PROP_PINNED, "pin"}, //
{RULE_PROP_FOCUS, "focus"}, //
{RULE_PROP_GROUP, "group"}, //
{RULE_PROP_MODAL, "modal"}, //
{RULE_PROP_FULLSCREENSTATE_INTERNAL, "fullscreen_state_internal"}, //
{RULE_PROP_FULLSCREENSTATE_CLIENT, "fullscreen_state_client"}, //
{RULE_PROP_ON_WORKSPACE, "workspace"}, //
{RULE_PROP_CONTENT, "content"}, //
{RULE_PROP_XDG_TAG, "xdg_tag"}, //
{RULE_PROP_NAMESPACE, "namespace"}, //
};
static const std::unordered_map<eRuleProperty, eRuleMatchEngine> RULE_ENGINES = {
{RULE_PROP_CLASS, RULE_MATCH_ENGINE_REGEX}, //
{RULE_PROP_TITLE, RULE_MATCH_ENGINE_REGEX}, //
{RULE_PROP_INITIAL_CLASS, RULE_MATCH_ENGINE_REGEX}, //
{RULE_PROP_INITIAL_TITLE, RULE_MATCH_ENGINE_REGEX}, //
{RULE_PROP_FLOATING, RULE_MATCH_ENGINE_BOOL}, //
{RULE_PROP_TAG, RULE_MATCH_ENGINE_TAG}, //
{RULE_PROP_XWAYLAND, RULE_MATCH_ENGINE_BOOL}, //
{RULE_PROP_FULLSCREEN, RULE_MATCH_ENGINE_BOOL}, //
{RULE_PROP_PINNED, RULE_MATCH_ENGINE_BOOL}, //
{RULE_PROP_FOCUS, RULE_MATCH_ENGINE_BOOL}, //
{RULE_PROP_GROUP, RULE_MATCH_ENGINE_BOOL}, //
{RULE_PROP_MODAL, RULE_MATCH_ENGINE_BOOL}, //
{RULE_PROP_FULLSCREENSTATE_INTERNAL, RULE_MATCH_ENGINE_INT}, //
{RULE_PROP_FULLSCREENSTATE_CLIENT, RULE_MATCH_ENGINE_INT}, //
{RULE_PROP_ON_WORKSPACE, RULE_MATCH_ENGINE_WORKSPACE}, //
{RULE_PROP_CONTENT, RULE_MATCH_ENGINE_INT}, //
{RULE_PROP_XDG_TAG, RULE_MATCH_ENGINE_REGEX}, //
{RULE_PROP_NAMESPACE, RULE_MATCH_ENGINE_REGEX}, //
{RULE_PROP_EXEC_TOKEN, RULE_MATCH_ENGINE_REGEX}, //
};
const std::vector<std::string>& Rule::allMatchPropStrings() {
static std::vector<std::string> strings;
static bool once = true;
if (once) {
for (const auto& [k, v] : MATCH_PROP_STRINGS) {
strings.emplace_back(v);
}
once = false;
}
return strings;
}
std::optional<eRuleProperty> Rule::matchPropFromString(const std::string_view& s) {
const auto IT = std::ranges::find_if(MATCH_PROP_STRINGS, [&s](const auto& el) { return el.second == s; });
if (IT == MATCH_PROP_STRINGS.end())
return std::nullopt;
return IT->first;
}
std::optional<eRuleProperty> Rule::matchPropFromString(const std::string& s) {
return matchPropFromString(std::string_view{s});
}
IRule::IRule(const std::string& name) : m_name(name) {
;
}
void IRule::registerMatch(eRuleProperty p, const std::string& s) {
if (!RULE_ENGINES.contains(p)) {
Debug::log(ERR, "BUG THIS: IRule: RULE_ENGINES does not contain rule idx {}", sc<std::underlying_type_t<eRuleProperty>>(p));
return;
}
switch (RULE_ENGINES.at(p)) {
case RULE_MATCH_ENGINE_REGEX: m_matchEngines[p] = makeUnique<CRegexMatchEngine>(s); break;
case RULE_MATCH_ENGINE_BOOL: m_matchEngines[p] = makeUnique<CBoolMatchEngine>(s); break;
case RULE_MATCH_ENGINE_INT: m_matchEngines[p] = makeUnique<CIntMatchEngine>(s); break;
case RULE_MATCH_ENGINE_WORKSPACE: m_matchEngines[p] = makeUnique<CWorkspaceMatchEngine>(s); break;
case RULE_MATCH_ENGINE_TAG: m_matchEngines[p] = makeUnique<CTagMatchEngine>(s); break;
}
m_mask |= p;
}
std::underlying_type_t<eRuleProperty> IRule::getPropertiesMask() {
return m_mask;
}
bool IRule::has(eRuleProperty p) {
return m_matchEngines.contains(p);
}
bool IRule::matches(eRuleProperty p, const std::string& s) {
if (!has(p))
return false;
return m_matchEngines[p]->match(s);
}
bool IRule::matches(eRuleProperty p, bool b) {
if (!has(p))
return false;
return m_matchEngines[p]->match(b);
}
const std::string& IRule::name() {
return m_name;
}
void IRule::markAsExecRule(const std::string& token, bool persistent) {
m_execData.isExecRule = true;
m_execData.isExecPersistent = persistent;
m_execData.token = token;
m_execData.expiresAt = Time::steadyNow() + std::chrono::minutes(1);
}
bool IRule::isExecRule() {
return m_execData.isExecRule;
}
bool IRule::isExecPersistent() {
return m_execData.isExecPersistent;
}
bool IRule::execExpired() {
return Time::steadyNow() > m_execData.expiresAt;
}
const std::string& IRule::execToken() {
return m_execData.token;
}

84
src/desktop/rule/Rule.hpp Normal file
View file

@ -0,0 +1,84 @@
#pragma once
#include "matchEngine/MatchEngine.hpp"
#include "../../helpers/memory/Memory.hpp"
#include "../../helpers/time/Time.hpp"
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <optional>
namespace Desktop::Rule {
enum eRuleProperty : uint32_t {
RULE_PROP_NONE = 0,
RULE_PROP_CLASS = (1 << 0),
RULE_PROP_TITLE = (1 << 1),
RULE_PROP_INITIAL_CLASS = (1 << 2),
RULE_PROP_INITIAL_TITLE = (1 << 3),
RULE_PROP_FLOATING = (1 << 4),
RULE_PROP_TAG = (1 << 5),
RULE_PROP_XWAYLAND = (1 << 6),
RULE_PROP_FULLSCREEN = (1 << 7),
RULE_PROP_PINNED = (1 << 8),
RULE_PROP_FOCUS = (1 << 9),
RULE_PROP_GROUP = (1 << 10),
RULE_PROP_MODAL = (1 << 11),
RULE_PROP_FULLSCREENSTATE_INTERNAL = (1 << 12),
RULE_PROP_FULLSCREENSTATE_CLIENT = (1 << 13),
RULE_PROP_ON_WORKSPACE = (1 << 14),
RULE_PROP_CONTENT = (1 << 15),
RULE_PROP_XDG_TAG = (1 << 16),
RULE_PROP_NAMESPACE = (1 << 17),
RULE_PROP_EXEC_TOKEN = (1 << 18),
RULE_PROP_ALL = std::numeric_limits<std::underlying_type_t<eRuleProperty>>::max(),
};
enum eRuleType : uint8_t {
RULE_TYPE_WINDOW = 0,
RULE_TYPE_LAYER,
};
std::optional<eRuleProperty> matchPropFromString(const std::string& s);
std::optional<eRuleProperty> matchPropFromString(const std::string_view& s);
const std::vector<std::string>& allMatchPropStrings();
class IRule {
public:
virtual ~IRule() = default;
virtual eRuleType type() = 0;
virtual std::underlying_type_t<eRuleProperty> getPropertiesMask();
void registerMatch(eRuleProperty, const std::string&);
void markAsExecRule(const std::string& token, bool persistent = false);
bool isExecRule();
bool isExecPersistent();
bool execExpired();
const std::string& execToken();
const std::string& name();
protected:
IRule(const std::string& name = "");
bool matches(eRuleProperty, const std::string& s);
bool matches(eRuleProperty, bool b);
bool has(eRuleProperty);
//
std::unordered_map<eRuleProperty, UP<IMatchEngine>> m_matchEngines;
private:
std::underlying_type_t<eRuleProperty> m_mask = 0;
std::string m_name = "";
struct {
bool isExecRule = false;
bool isExecPersistent = false;
std::string token;
Time::steady_tp expiresAt;
} m_execData;
};
}

View file

@ -0,0 +1,81 @@
#pragma once
#include <string>
#include <vector>
#include <type_traits>
#include <cstdint>
#include <optional>
namespace Desktop::Rule {
template <typename T>
class IEffectContainer {
static_assert(std::is_enum_v<T>);
protected:
const std::string DEFAULT_MISSING_KEY = "";
public:
// Make sure we're using at least a uint16_t for dynamic registrations to not overflow.
// 32k should be enough
using storageType = std::conditional_t<(sizeof(std::underlying_type_t<T>) >= 2), std::underlying_type_t<T>, uint16_t>;
IEffectContainer(std::vector<std::string>&& defaultKeys) : m_keys(std::move(defaultKeys)), m_originalSize(m_keys.size()) {
;
}
virtual ~IEffectContainer() = default;
virtual storageType registerEffect(std::string&& name) {
if (m_keys.size() >= std::numeric_limits<storageType>::max())
return 0;
if (auto it = std::ranges::find(m_keys, name); it != m_keys.end())
return it - m_keys.begin();
m_keys.emplace_back(std::move(name));
return m_keys.size() - 1;
}
virtual void unregisterEffect(storageType id) {
if (id >= m_keys.size())
return;
m_keys[id] = DEFAULT_MISSING_KEY;
}
virtual void unregisterEffect(const std::string& name) {
for (auto& key : m_keys) {
if (key == name) {
key = DEFAULT_MISSING_KEY;
break;
}
}
}
virtual const std::string& get(storageType idx) {
if (idx >= m_keys.size())
return DEFAULT_MISSING_KEY;
return m_keys[idx];
}
virtual std::optional<storageType> get(const std::string_view& s) {
for (storageType i = 0; i < m_keys.size(); ++i) {
if (m_keys[i] == s)
return i;
}
return std::nullopt;
}
virtual const std::vector<std::string>& allEffectStrings() {
return m_keys;
}
// whether the effect has been added dynamically as opposed to in the ctor.
virtual bool isEffectDynamic(storageType i) {
return i >= m_originalSize;
}
protected:
std::vector<std::string> m_keys;
size_t m_originalSize = 0;
};
};

View file

@ -0,0 +1,43 @@
#include "LayerRule.hpp"
#include "../../../debug/Log.hpp"
#include "../../LayerSurface.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
CLayerRule::CLayerRule(const std::string& name) : IRule(name) {
;
}
eRuleType CLayerRule::type() {
return RULE_TYPE_LAYER;
}
void CLayerRule::addEffect(CLayerRule::storageType e, const std::string& result) {
m_effects.emplace_back(std::make_pair<>(e, result));
}
const std::vector<std::pair<CLayerRule::storageType, std::string>>& CLayerRule::effects() {
return m_effects;
}
bool CLayerRule::matches(PHLLS ls) {
if (m_matchEngines.empty())
return false;
for (const auto& [prop, engine] : m_matchEngines) {
switch (prop) {
default: {
Debug::log(TRACE, "CLayerRule::matches: skipping prop entry {}", sc<std::underlying_type_t<eRuleProperty>>(prop));
break;
}
case RULE_PROP_NAMESPACE:
if (!engine->match(ls->m_namespace))
return false;
break;
}
}
return true;
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "../Rule.hpp"
#include "../../DesktopTypes.hpp"
#include "LayerRuleEffectContainer.hpp"
namespace Desktop::Rule {
class CLayerRule : public IRule {
public:
using storageType = CLayerRuleEffectContainer::storageType;
CLayerRule(const std::string& name = "");
virtual ~CLayerRule() = default;
virtual eRuleType type();
void addEffect(storageType e, const std::string& result);
const std::vector<std::pair<storageType, std::string>>& effects();
bool matches(PHLLS w);
private:
std::vector<std::pair<storageType, std::string>> m_effects;
};
};

View file

@ -0,0 +1,128 @@
#include "LayerRuleApplicator.hpp"
#include "LayerRule.hpp"
#include "../Engine.hpp"
#include "../../LayerSurface.hpp"
#include "../../types/OverridableVar.hpp"
#include "../../../helpers/MiscFunctions.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
CLayerRuleApplicator::CLayerRuleApplicator(PHLLS ls) : m_ls(ls) {
;
}
void CLayerRuleApplicator::resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio) {
// TODO: fucking kill me, is there a better way to do this?
#define UNSET(x) \
if (m_##x.second & props) { \
if (prio == Types::PRIORITY_WINDOW_RULE) \
m_##x.second &= ~props; \
m_##x.first.unset(prio); \
}
UNSET(noanim)
UNSET(blur)
UNSET(blurPopups)
UNSET(dimAround)
UNSET(xray)
UNSET(noScreenShare)
UNSET(order)
UNSET(aboveLock)
UNSET(ignoreAlpha)
UNSET(animationStyle)
}
void CLayerRuleApplicator::applyDynamicRule(const SP<CLayerRule>& rule) {
for (const auto& [key, effect] : rule->effects()) {
switch (key) {
case LAYER_RULE_EFFECT_NONE: {
Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: BUG THIS: LAYER_RULE_EFFECT_NONE??");
break;
}
case LAYER_RULE_EFFECT_NO_ANIM: {
m_noanim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noanim.second |= rule->getPropertiesMask();
break;
}
case LAYER_RULE_EFFECT_BLUR: {
m_blur.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_blur.second |= rule->getPropertiesMask();
break;
}
case LAYER_RULE_EFFECT_BLUR_POPUPS: {
m_blurPopups.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_blurPopups.second |= rule->getPropertiesMask();
break;
}
case LAYER_RULE_EFFECT_DIM_AROUND: {
m_dimAround.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_dimAround.second |= rule->getPropertiesMask();
break;
}
case LAYER_RULE_EFFECT_XRAY: {
m_xray.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_xray.second |= rule->getPropertiesMask();
break;
}
case LAYER_RULE_EFFECT_NO_SCREEN_SHARE: {
m_noScreenShare.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noScreenShare.second |= rule->getPropertiesMask();
break;
}
case LAYER_RULE_EFFECT_ORDER: {
try {
m_noScreenShare.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE);
m_noScreenShare.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); }
break;
}
case LAYER_RULE_EFFECT_ABOVE_LOCK: {
try {
m_aboveLock.first.set(std::clamp(std::stoull(effect), 0ULL, 2ULL), Types::PRIORITY_WINDOW_RULE);
m_aboveLock.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); }
break;
}
case LAYER_RULE_EFFECT_IGNORE_ALPHA: {
try {
m_ignoreAlpha.first.set(std::clamp(std::stof(effect), 0.F, 1.F), Types::PRIORITY_WINDOW_RULE);
m_ignoreAlpha.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CLayerRuleApplicator::applyDynamicRule: invalid order {}", effect); }
break;
}
case LAYER_RULE_EFFECT_ANIMATION: {
m_animationStyle.first.set(effect, Types::PRIORITY_WINDOW_RULE);
m_animationStyle.second |= rule->getPropertiesMask();
break;
}
}
}
}
void CLayerRuleApplicator::propertiesChanged(std::underlying_type_t<eRuleProperty> props) {
if (!m_ls)
return;
resetProps(props);
// FIXME: this will not update properties correctly if we implement dynamic rules for
// layers, due to effects overlapping on 0 prop intersection.
// See WindowRule.cpp, and ::propertiesChanged there.
for (const auto& r : ruleEngine()->rules()) {
if (r->type() != RULE_TYPE_LAYER)
continue;
if (!(r->getPropertiesMask() & props))
continue;
auto wr = reinterpretPointerCast<CLayerRule>(r);
if (!wr->matches(m_ls.lock()))
continue;
applyDynamicRule(wr);
}
}

View file

@ -0,0 +1,60 @@
#pragma once
#include "../../DesktopTypes.hpp"
#include "../Rule.hpp"
#include "../../types/OverridableVar.hpp"
#include "../../../helpers/math/Math.hpp"
#include "../../../config/ConfigDataValues.hpp"
namespace Desktop::Rule {
class CLayerRule;
class CLayerRuleApplicator {
public:
CLayerRuleApplicator(PHLLS ls);
~CLayerRuleApplicator() = default;
CLayerRuleApplicator(const CLayerRuleApplicator&) = delete;
CLayerRuleApplicator(CLayerRuleApplicator&) = delete;
CLayerRuleApplicator(CLayerRuleApplicator&&) = delete;
void propertiesChanged(std::underlying_type_t<eRuleProperty> props);
void resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE);
#define COMMA ,
#define DEFINE_PROP(type, name, def) \
private: \
std::pair<Types::COverridableVar<type>, std::underlying_type_t<eRuleProperty>> m_##name = {def, RULE_PROP_NONE}; \
\
public: \
Types::COverridableVar<type>& name() { \
return m_##name.first; \
} \
void name##Override(const Types::COverridableVar<type>& other) { \
m_##name.first = other; \
}
// dynamic props
DEFINE_PROP(bool, noanim, false)
DEFINE_PROP(bool, blur, false)
DEFINE_PROP(bool, blurPopups, false)
DEFINE_PROP(bool, dimAround, false)
DEFINE_PROP(bool, xray, false)
DEFINE_PROP(bool, noScreenShare, false)
DEFINE_PROP(Hyprlang::INT, order, 0)
DEFINE_PROP(Hyprlang::INT, aboveLock, 0)
DEFINE_PROP(Hyprlang::FLOAT, ignoreAlpha, 0.F)
DEFINE_PROP(std::string, animationStyle, std::string(""))
#undef COMMA
#undef DEFINE_PROP
private:
PHLLSREF m_ls;
void applyDynamicRule(const SP<CLayerRule>& rule);
};
};

View file

@ -0,0 +1,33 @@
#include "LayerRuleEffectContainer.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
//
SP<CLayerRuleEffectContainer> Rule::layerEffects() {
static SP<CLayerRuleEffectContainer> container = makeShared<CLayerRuleEffectContainer>();
return container;
}
static const std::vector<std::string> EFFECT_STRINGS = {
"__internal_none", //
"no_anim", //
"blur", //
"blur_popups", //
"ignore_alpha", //
"dim_around", //
"xray", //
"animation", //
"order", //
"above_lock", //
"no_screen_share", //
"__internal_last_static", //
};
// This is here so that if we change the rules, we get reminded to update
// the strings.
static_assert(LAYER_RULE_EFFECT_LAST_STATIC == 11);
CLayerRuleEffectContainer::CLayerRuleEffectContainer() : IEffectContainer<eLayerRuleEffect>(std::vector<std::string>{EFFECT_STRINGS}) {
;
}

View file

@ -0,0 +1,33 @@
#pragma once
#include "../effect/EffectContainer.hpp"
#include "../../../helpers/memory/Memory.hpp"
#pragma once
namespace Desktop::Rule {
enum eLayerRuleEffect : uint8_t {
LAYER_RULE_EFFECT_NONE = 0,
LAYER_RULE_EFFECT_NO_ANIM,
LAYER_RULE_EFFECT_BLUR,
LAYER_RULE_EFFECT_BLUR_POPUPS,
LAYER_RULE_EFFECT_IGNORE_ALPHA,
LAYER_RULE_EFFECT_DIM_AROUND,
LAYER_RULE_EFFECT_XRAY,
LAYER_RULE_EFFECT_ANIMATION,
LAYER_RULE_EFFECT_ORDER,
LAYER_RULE_EFFECT_ABOVE_LOCK,
LAYER_RULE_EFFECT_NO_SCREEN_SHARE,
LAYER_RULE_EFFECT_LAST_STATIC,
};
class CLayerRuleEffectContainer : public IEffectContainer<eLayerRuleEffect> {
public:
CLayerRuleEffectContainer();
virtual ~CLayerRuleEffectContainer() = default;
};
SP<CLayerRuleEffectContainer> layerEffects();
};

View file

@ -0,0 +1,12 @@
#include "BoolMatchEngine.hpp"
#include "../../../helpers/MiscFunctions.hpp"
using namespace Desktop::Rule;
CBoolMatchEngine::CBoolMatchEngine(const std::string& s) : m_value(truthy(s)) {
;
}
bool CBoolMatchEngine::match(bool other) {
return other == m_value;
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "MatchEngine.hpp"
namespace Desktop::Rule {
class CBoolMatchEngine : public IMatchEngine {
public:
CBoolMatchEngine(const std::string&);
virtual ~CBoolMatchEngine() = default;
virtual bool match(bool other);
private:
bool m_value = false;
};
}

View file

@ -0,0 +1,14 @@
#include "IntMatchEngine.hpp"
#include "../../../debug/Log.hpp"
using namespace Desktop::Rule;
CIntMatchEngine::CIntMatchEngine(const std::string& s) {
try {
m_value = std::stoi(s);
} catch (...) { Debug::log(ERR, "CIntMatchEngine: invalid input {}", s); }
}
bool CIntMatchEngine::match(int other) {
return m_value == other;
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "MatchEngine.hpp"
namespace Desktop::Rule {
class CIntMatchEngine : public IMatchEngine {
public:
CIntMatchEngine(const std::string&);
virtual ~CIntMatchEngine() = default;
virtual bool match(int other);
private:
int m_value = 0;
};
}

View file

@ -0,0 +1,23 @@
#include "MatchEngine.hpp"
using namespace Desktop::Rule;
bool IMatchEngine::match(const std::string&) {
return false;
}
bool IMatchEngine::match(bool) {
return false;
}
bool IMatchEngine::match(int) {
return false;
}
bool IMatchEngine::match(PHLWORKSPACE) {
return false;
}
bool IMatchEngine::match(const CTagKeeper& keeper) {
return false;
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "../../DesktopTypes.hpp"
class CTagKeeper;
namespace Desktop::Rule {
enum eRuleMatchEngine : uint8_t {
RULE_MATCH_ENGINE_REGEX = 0,
RULE_MATCH_ENGINE_BOOL,
RULE_MATCH_ENGINE_INT,
RULE_MATCH_ENGINE_WORKSPACE,
RULE_MATCH_ENGINE_TAG,
};
class IMatchEngine {
public:
virtual ~IMatchEngine() = default;
virtual bool match(const std::string&);
virtual bool match(bool);
virtual bool match(int);
virtual bool match(PHLWORKSPACE);
virtual bool match(const CTagKeeper& keeper);
protected:
IMatchEngine() = default;
};
};

View file

@ -0,0 +1,17 @@
#include "RegexMatchEngine.hpp"
#include <re2/re2.h>
using namespace Desktop::Rule;
CRegexMatchEngine::CRegexMatchEngine(const std::string& regex) {
if (regex.starts_with("negative:")) {
m_negative = true;
m_regex = makeUnique<re2::RE2>(regex.substr(9));
return;
}
m_regex = makeUnique<re2::RE2>(regex);
}
bool CRegexMatchEngine::match(const std::string& other) {
return re2::RE2::FullMatch(other, *m_regex) != m_negative;
}

View file

@ -0,0 +1,23 @@
#pragma once
#include "MatchEngine.hpp"
#include "../../../helpers/memory/Memory.hpp"
//NOLINTNEXTLINE
namespace re2 {
class RE2;
};
namespace Desktop::Rule {
class CRegexMatchEngine : public IMatchEngine {
public:
CRegexMatchEngine(const std::string& regex);
virtual ~CRegexMatchEngine() = default;
virtual bool match(const std::string& other);
private:
UP<re2::RE2> m_regex;
bool m_negative = false;
};
}

View file

@ -0,0 +1,12 @@
#include "TagMatchEngine.hpp"
#include "../../../helpers/TagKeeper.hpp"
using namespace Desktop::Rule;
CTagMatchEngine::CTagMatchEngine(const std::string& tag) : m_tag(tag) {
;
}
bool CTagMatchEngine::match(const CTagKeeper& keeper) {
return keeper.isTagged(m_tag);
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "MatchEngine.hpp"
namespace Desktop::Rule {
class CTagMatchEngine : public IMatchEngine {
public:
CTagMatchEngine(const std::string& tag);
virtual ~CTagMatchEngine() = default;
virtual bool match(const CTagKeeper& keeper);
private:
std::string m_tag;
};
}

View file

@ -0,0 +1,12 @@
#include "WorkspaceMatchEngine.hpp"
#include "../../Workspace.hpp"
using namespace Desktop::Rule;
CWorkspaceMatchEngine::CWorkspaceMatchEngine(const std::string& s) : m_value(s) {
;
}
bool CWorkspaceMatchEngine::match(PHLWORKSPACE ws) {
return ws->matchesStaticSelector(m_value);
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "MatchEngine.hpp"
namespace Desktop::Rule {
class CWorkspaceMatchEngine : public IMatchEngine {
public:
CWorkspaceMatchEngine(const std::string&);
virtual ~CWorkspaceMatchEngine() = default;
virtual bool match(PHLWORKSPACE ws);
private:
std::string m_value = "";
};
}

View file

@ -0,0 +1,17 @@
#pragma once
#include <unordered_set>
namespace Desktop::Rule {
template <class T>
bool setsIntersect(const std::unordered_set<T>& A, const std::unordered_set<T>& B) {
if (A.size() > B.size())
return setsIntersect(B, A);
for (const auto& e : A) {
if (B.contains(e))
return true;
}
return false;
}
};

View file

@ -0,0 +1,186 @@
#include "WindowRule.hpp"
#include "../../Window.hpp"
#include "../../../helpers/Monitor.hpp"
#include "../../../Compositor.hpp"
#include "../../../managers/TokenManager.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
std::optional<Vector2D> Rule::parseRelativeVector(PHLWINDOW w, const std::string& s) {
try {
const auto VALUE = s.substr(s.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const auto MAXSIZE = w->requestedMaxSize();
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, g_pCompositor->m_lastMonitor->m_size.x) :
stringToPercentage(SIZEXSTR, g_pCompositor->m_lastMonitor->m_size.x);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, g_pCompositor->m_lastMonitor->m_size.y) :
stringToPercentage(SIZEYSTR, g_pCompositor->m_lastMonitor->m_size.y);
return Vector2D{SIZEX, SIZEY};
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {}", s); }
return std::nullopt;
}
CWindowRule::CWindowRule(const std::string& name) : IRule(name) {
;
}
eRuleType CWindowRule::type() {
return RULE_TYPE_WINDOW;
}
void CWindowRule::addEffect(CWindowRule::storageType e, const std::string& result) {
m_effects.emplace_back(std::make_pair<>(e, result));
m_effectSet.emplace(e);
}
const std::vector<std::pair<CWindowRule::storageType, std::string>>& CWindowRule::effects() {
return m_effects;
}
bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) {
if (m_matchEngines.empty())
return false;
for (const auto& [prop, engine] : m_matchEngines) {
switch (prop) {
default: {
Debug::log(TRACE, "CWindowRule::matches: skipping prop entry {}", sc<std::underlying_type_t<eRuleProperty>>(prop));
break;
}
case RULE_PROP_TITLE:
if (!engine->match(w->m_title))
return false;
break;
case RULE_PROP_INITIAL_TITLE:
if (!engine->match(w->m_initialTitle))
return false;
break;
case RULE_PROP_CLASS:
if (!engine->match(w->m_class))
return false;
break;
case RULE_PROP_INITIAL_CLASS:
if (!engine->match(w->m_initialClass))
return false;
break;
case RULE_PROP_FLOATING:
if (!engine->match(w->m_isFloating))
return false;
break;
case RULE_PROP_TAG:
if (!engine->match(w->m_ruleApplicator->m_tagKeeper))
return false;
break;
case RULE_PROP_XWAYLAND:
if (!engine->match(w->m_isX11))
return false;
break;
case RULE_PROP_FULLSCREEN:
if (!engine->match(w->m_fullscreenState.internal != 0))
return false;
break;
case RULE_PROP_PINNED:
if (!engine->match(w->m_pinned))
return false;
break;
case RULE_PROP_FOCUS:
if (!engine->match(g_pCompositor->m_lastWindow == w))
return false;
break;
case RULE_PROP_GROUP:
if (!engine->match(w->m_groupData.pNextWindow))
return false;
break;
case RULE_PROP_MODAL:
if (!engine->match(w->isModal()))
return false;
break;
case RULE_PROP_FULLSCREENSTATE_INTERNAL:
if (!engine->match(w->m_fullscreenState.internal))
return false;
break;
case RULE_PROP_FULLSCREENSTATE_CLIENT:
if (!engine->match(w->m_fullscreenState.client))
return false;
break;
case RULE_PROP_ON_WORKSPACE:
if (!engine->match(w->m_workspace))
return false;
break;
case RULE_PROP_CONTENT:
if (!engine->match(NContentType::toString(w->getContentType())))
return false;
break;
case RULE_PROP_XDG_TAG:
if (!w->xdgTag().has_value() || !engine->match(*w->xdgTag()))
return false;
break;
case RULE_PROP_EXEC_TOKEN:
// this is only allowed on static rules, we don't need it on dynamic plus it's expensive
if (!allowEnvLookup)
break;
const auto ENV = w->getEnv();
if (ENV.contains(EXEC_RULE_ENV_NAME)) {
const auto TKN = ENV.at(EXEC_RULE_ENV_NAME);
if (!engine->match(TKN))
return false;
break;
}
return false;
}
}
return true;
}
SP<CWindowRule> CWindowRule::buildFromExecString(std::string&& s) {
CVarList2 varlist(std::move(s), 0, ';');
SP<CWindowRule> wr = makeShared<CWindowRule>("__exec_rule");
for (const auto& el : varlist) {
// split element by space, can't do better
size_t spacePos = el.find(' ');
if (spacePos != std::string::npos) {
// great, split and try to parse
auto LHS = el.substr(0, spacePos);
const auto EFFECT = windowEffects()->get(LHS);
if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE)
continue; // invalid...
wr->addEffect(*EFFECT, std::string{el.substr(spacePos + 1)});
continue;
}
// assume 1 maybe...
const auto EFFECT = windowEffects()->get(el);
if (!EFFECT.has_value() || *EFFECT == WINDOW_RULE_EFFECT_NONE)
continue; // invalid...
wr->addEffect(*EFFECT, std::string{"1"});
}
const auto TOKEN = g_pTokenManager->registerNewToken(nullptr, std::chrono::seconds(1));
wr->markAsExecRule(TOKEN, false /* TODO: could be nice. */);
wr->registerMatch(RULE_PROP_EXEC_TOKEN, TOKEN);
return wr;
}
const std::unordered_set<CWindowRule::storageType>& CWindowRule::effectsSet() {
return m_effectSet;
}

View file

@ -0,0 +1,37 @@
#pragma once
#include "../Rule.hpp"
#include "../../DesktopTypes.hpp"
#include "WindowRuleEffectContainer.hpp"
#include "../../../helpers/math/Math.hpp"
#include <unordered_set>
namespace Desktop::Rule {
constexpr const char* EXEC_RULE_ENV_NAME = "HL_EXEC_RULE_TOKEN";
std::optional<Vector2D> parseRelativeVector(PHLWINDOW w, const std::string& s);
class CWindowRule : public IRule {
private:
using storageType = CWindowRuleEffectContainer::storageType;
public:
CWindowRule(const std::string& name = "");
virtual ~CWindowRule() = default;
static SP<CWindowRule> buildFromExecString(std::string&&);
virtual eRuleType type();
void addEffect(storageType e, const std::string& result);
const std::vector<std::pair<storageType, std::string>>& effects();
const std::unordered_set<storageType>& effectsSet();
bool matches(PHLWINDOW w, bool allowEnvLookup = false);
private:
std::vector<std::pair<storageType, std::string>> m_effects;
std::unordered_set<storageType> m_effectSet;
};
};

View file

@ -0,0 +1,646 @@
#include "WindowRuleApplicator.hpp"
#include "WindowRule.hpp"
#include "../Engine.hpp"
#include "../utils/SetUtils.hpp"
#include "../../Window.hpp"
#include "../../types/OverridableVar.hpp"
#include "../../../managers/LayoutManager.hpp"
#include "../../../managers/HookSystemManager.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
using namespace Desktop;
using namespace Desktop::Rule;
CWindowRuleApplicator::CWindowRuleApplicator(PHLWINDOW w) : m_window(w) {
;
}
void CWindowRuleApplicator::resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio) {
// TODO: fucking kill me, is there a better way to do this?
#define UNSET(x) \
if (m_##x.second & props) { \
if (prio == Types::PRIORITY_WINDOW_RULE) \
m_##x.second &= ~props; \
m_##x.first.unset(prio); \
}
UNSET(alpha)
UNSET(alphaInactive)
UNSET(alphaFullscreen)
UNSET(allowsInput)
UNSET(decorate)
UNSET(focusOnActivate)
UNSET(keepAspectRatio)
UNSET(nearestNeighbor)
UNSET(noAnim)
UNSET(noBlur)
UNSET(noDim)
UNSET(noFocus)
UNSET(noMaxSize)
UNSET(noShadow)
UNSET(noShortcutsInhibit)
UNSET(opaque)
UNSET(dimAround)
UNSET(RGBX)
UNSET(syncFullscreen)
UNSET(tearing)
UNSET(xray)
UNSET(renderUnfocused)
UNSET(noFollowMouse)
UNSET(noScreenShare)
UNSET(noVRR)
UNSET(persistentSize)
UNSET(stayFocused)
UNSET(idleInhibitMode)
UNSET(borderSize)
UNSET(rounding)
UNSET(roundingPower)
UNSET(scrollMouse)
UNSET(scrollTouchpad)
UNSET(animationStyle)
UNSET(maxSize)
UNSET(minSize)
UNSET(activeBorderColor)
UNSET(inactiveBorderColor)
#undef UNSET
if (prio == Types::PRIORITY_WINDOW_RULE) {
std::erase_if(m_dynamicTags, [props, this](const auto& el) {
const bool REMOVE = el.second & props;
if (REMOVE)
m_tagKeeper.removeDynamicTag(el.first);
return REMOVE;
});
std::erase_if(m_otherProps.props, [props](const auto& el) { return !el.second || el.second->propMask & props; });
}
}
CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyDynamicRule(const SP<CWindowRule>& rule) {
SRuleResult result;
for (const auto& [key, effect] : rule->effects()) {
switch (key) {
default: {
if (key <= WINDOW_RULE_EFFECT_LAST_STATIC) {
Debug::log(TRACE, "CWindowRuleApplicator::applyDynamicRule: Skipping effect {}, not dynamic", sc<std::underlying_type_t<eWindowRuleEffect>>(key));
break;
}
// custom type, add to our vec
if (!m_otherProps.props.contains(key)) {
m_otherProps.props.emplace(key,
makeUnique<SCustomPropContainer>(SCustomPropContainer{
.idx = key,
.propMask = rule->getPropertiesMask(),
.effect = effect,
}));
} else {
auto& e = m_otherProps.props[key];
e->propMask |= rule->getPropertiesMask();
e->effect = effect;
}
break;
}
case WINDOW_RULE_EFFECT_NONE: {
Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: BUG THIS: WINDOW_RULE_EFFECT_NONE??");
break;
}
case WINDOW_RULE_EFFECT_ROUNDING: {
try {
m_rounding.first.set(std::stoull(effect), Types::PRIORITY_WINDOW_RULE);
m_rounding.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding {}", effect); }
break;
}
case WINDOW_RULE_EFFECT_ROUNDING_POWER: {
try {
m_roundingPower.first.set(std::clamp(std::stof(effect), 1.F, 10.F), Types::PRIORITY_WINDOW_RULE);
m_roundingPower.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding_power {}", effect); }
break;
}
case WINDOW_RULE_EFFECT_PERSISTENT_SIZE: {
try {
m_persistentSize.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE);
m_persistentSize.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid rounding_power {}", effect); }
break;
}
case WINDOW_RULE_EFFECT_ANIMATION: {
m_animationStyle.first.set(effect, Types::PRIORITY_WINDOW_RULE);
m_animationStyle.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_BORDER_COLOR: {
try {
// Each vector will only get used if it has at least one color
CGradientValueData activeBorderGradient = {};
CGradientValueData inactiveBorderGradient = {};
bool active = true;
CVarList colorsAndAngles = CVarList(trim(effect.substr(effect.find_first_of(' ') + 1)), 0, 's', true);
// Basic form has only two colors, everything else can be parsed as a gradient
if (colorsAndAngles.size() == 2 && !colorsAndAngles[1].contains("deg")) {
m_activeBorderColor.first =
Types::COverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[0]).value_or(0))), Types::PRIORITY_WINDOW_RULE);
m_inactiveBorderColor.first =
Types::COverridableVar(CGradientValueData(CHyprColor(configStringToInt(colorsAndAngles[1]).value_or(0))), Types::PRIORITY_WINDOW_RULE);
break;
}
for (auto const& token : colorsAndAngles) {
// The first angle, or an explicit "0deg", splits the two gradients
if (active && token.contains("deg")) {
activeBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
active = false;
} else if (token.contains("deg"))
inactiveBorderGradient.m_angle = std::stoi(token.substr(0, token.size() - 3)) * (PI / 180.0);
else if (active)
activeBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0));
else
inactiveBorderGradient.m_colors.emplace_back(configStringToInt(token).value_or(0));
}
activeBorderGradient.updateColorsOk();
// Includes sanity checks for the number of colors in each gradient
if (activeBorderGradient.m_colors.size() > 10 || inactiveBorderGradient.m_colors.size() > 10)
Debug::log(WARN, "Bordercolor rule \"{}\" has more than 10 colors in one gradient, ignoring", effect);
else if (activeBorderGradient.m_colors.empty())
Debug::log(WARN, "Bordercolor rule \"{}\" has no colors, ignoring", effect);
else if (inactiveBorderGradient.m_colors.empty())
m_activeBorderColor.first = Types::COverridableVar(activeBorderGradient, Types::PRIORITY_WINDOW_RULE);
else {
m_activeBorderColor.first = Types::COverridableVar(activeBorderGradient, Types::PRIORITY_WINDOW_RULE);
m_inactiveBorderColor.first = Types::COverridableVar(inactiveBorderGradient, Types::PRIORITY_WINDOW_RULE);
}
} catch (std::exception& e) { Debug::log(ERR, "BorderColor rule \"{}\" failed with: {}", effect, e.what()); }
m_activeBorderColor.second = rule->getPropertiesMask();
m_inactiveBorderColor.second = rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_IDLE_INHIBIT: {
if (effect == "none")
m_idleInhibitMode.first.set(IDLEINHIBIT_NONE, Types::PRIORITY_WINDOW_RULE);
else if (effect == "always")
m_idleInhibitMode.first.set(IDLEINHIBIT_ALWAYS, Types::PRIORITY_WINDOW_RULE);
else if (effect == "focus")
m_idleInhibitMode.first.set(IDLEINHIBIT_FOCUS, Types::PRIORITY_WINDOW_RULE);
else if (effect == "fullscreen")
m_idleInhibitMode.first.set(IDLEINHIBIT_FULLSCREEN, Types::PRIORITY_WINDOW_RULE);
else
Debug::log(ERR, "Rule idleinhibit: unknown mode {}", effect);
m_idleInhibitMode.second = rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_OPACITY: {
try {
CVarList2 vars(std::string{effect}, 0, ' ');
int opacityIDX = 0;
for (const auto& r : vars) {
if (r == "opacity")
continue;
if (r == "override") {
if (opacityIDX == 1)
m_alpha.first = Types::COverridableVar(Types::SAlphaValue{.alpha = m_alpha.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE);
else if (opacityIDX == 2)
m_alphaInactive.first =
Types::COverridableVar(Types::SAlphaValue{.alpha = m_alphaInactive.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE);
else if (opacityIDX == 3)
m_alphaFullscreen.first =
Types::COverridableVar(Types::SAlphaValue{.alpha = m_alphaFullscreen.first.value().alpha, .overridden = true}, Types::PRIORITY_WINDOW_RULE);
} else {
if (opacityIDX == 0)
m_alpha.first = Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE);
else if (opacityIDX == 1)
m_alphaInactive.first =
Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE);
else if (opacityIDX == 2)
m_alphaFullscreen.first =
Types::COverridableVar(Types::SAlphaValue{.alpha = std::stof(std::string{r}), .overridden = false}, Types::PRIORITY_WINDOW_RULE);
else
throw std::runtime_error("more than 3 alpha values");
opacityIDX++;
}
}
if (opacityIDX == 1) {
m_alphaInactive.first = m_alpha.first;
m_alphaFullscreen.first = m_alpha.first;
}
} catch (std::exception& e) { Debug::log(ERR, "Opacity rule \"{}\" failed with: {}", effect, e.what()); }
m_alpha.second = rule->getPropertiesMask();
m_alphaInactive.second = rule->getPropertiesMask();
m_alphaFullscreen.second = rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_TAG: {
m_dynamicTags.emplace_back(std::make_pair<>(effect, rule->getPropertiesMask()));
m_tagKeeper.applyTag(effect, true);
result.tagsChanged = true;
break;
}
case WINDOW_RULE_EFFECT_MAX_SIZE: {
try {
static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
if (!m_window)
break;
if (!m_window->m_isFloating && !sc<bool>(*PCLAMP_TILED))
break;
const auto VEC = configStringToVector2D(effect);
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for maxsize");
break;
}
m_maxSize.first = Types::COverridableVar(VEC, Types::PRIORITY_WINDOW_RULE);
m_window->clampWindowSize(std::nullopt, m_maxSize.first.value());
} catch (std::exception& e) { Debug::log(ERR, "maxsize rule \"{}\" failed with: {}", effect, e.what()); }
m_maxSize.second = rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_MIN_SIZE: {
try {
static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
if (!m_window)
break;
if (!m_window->m_isFloating && !sc<bool>(*PCLAMP_TILED))
break;
const auto VEC = configStringToVector2D(effect);
if (VEC.x < 1 || VEC.y < 1) {
Debug::log(ERR, "Invalid size for maxsize");
break;
}
m_minSize.first = Types::COverridableVar(VEC, Types::PRIORITY_WINDOW_RULE);
m_window->clampWindowSize(std::nullopt, m_minSize.first.value());
} catch (std::exception& e) { Debug::log(ERR, "minsize rule \"{}\" failed with: {}", effect, e.what()); }
m_minSize.second = rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_BORDER_SIZE: {
try {
auto oldBorderSize = m_borderSize.first.valueOrDefault();
m_borderSize.first.set(std::stoi(effect), Types::PRIORITY_WINDOW_RULE);
m_borderSize.second |= rule->getPropertiesMask();
if (oldBorderSize != m_borderSize.first.valueOrDefault())
result.needsRelayout = true;
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid border_size {}", effect); }
break;
}
case WINDOW_RULE_EFFECT_ALLOWS_INPUT: {
m_allowsInput.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_allowsInput.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_DIM_AROUND: {
m_dimAround.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_dimAround.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_DECORATE: {
m_decorate.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_decorate.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE: {
m_focusOnActivate.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_focusOnActivate.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO: {
m_keepAspectRatio.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_keepAspectRatio.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR: {
m_nearestNeighbor.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_nearestNeighbor.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_ANIM: {
m_noAnim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noAnim.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_BLUR: {
m_noBlur.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noBlur.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_DIM: {
m_noDim.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noDim.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_FOCUS: {
m_noFocus.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noFocus.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE: {
m_noFollowMouse.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noFollowMouse.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_MAX_SIZE: {
m_noMaxSize.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noMaxSize.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_SHADOW: {
m_noShadow.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noShadow.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT: {
m_noShortcutsInhibit.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noShortcutsInhibit.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_OPAQUE: {
m_opaque.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_opaque.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_FORCE_RGBX: {
m_RGBX.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_RGBX.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_SYNC_FULLSCREEN: {
m_syncFullscreen.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_syncFullscreen.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_IMMEDIATE: {
m_tearing.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_tearing.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_XRAY: {
m_xray.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_xray.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_RENDER_UNFOCUSED: {
m_renderUnfocused.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_renderUnfocused.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_SCREEN_SHARE: {
m_noScreenShare.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noScreenShare.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_NO_VRR: {
m_noVRR.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_noVRR.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_STAY_FOCUSED: {
m_stayFocused.first.set(truthy(effect), Types::PRIORITY_WINDOW_RULE);
m_stayFocused.second |= rule->getPropertiesMask();
break;
}
case WINDOW_RULE_EFFECT_SCROLL_MOUSE: {
try {
m_scrollMouse.first.set(std::clamp(std::stof(effect), 0.01F, 10.F), Types::PRIORITY_WINDOW_RULE);
m_scrollMouse.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_mouse {}", effect); }
break;
}
case WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD: {
try {
m_scrollTouchpad.first.set(std::clamp(std::stof(effect), 0.01F, 10.F), Types::PRIORITY_WINDOW_RULE);
m_scrollTouchpad.second |= rule->getPropertiesMask();
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyDynamicRule: invalid scroll_touchpad {}", effect); }
break;
}
}
}
return result;
}
CWindowRuleApplicator::SRuleResult CWindowRuleApplicator::applyStaticRule(const SP<CWindowRule>& rule) {
for (const auto& [key, effect] : rule->effects()) {
switch (key) {
default: {
Debug::log(TRACE, "CWindowRuleApplicator::applyStaticRule: Skipping effect {}, not static", sc<std::underlying_type_t<eWindowRuleEffect>>(key));
break;
}
case WINDOW_RULE_EFFECT_FLOAT: {
static_.floating = truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_TILE: {
static_.floating = !truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_FULLSCREEN: {
static_.fullscreen = truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_MAXIMIZE: {
static_.maximize = truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_FULLSCREENSTATE: {
CVarList2 vars(std::string{effect}, 0, 's');
try {
static_.fullscreenStateInternal = std::stoi(std::string{vars[0]});
if (!vars[1].empty())
static_.fullscreenStateClient = std::stoi(std::string{vars[1]});
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyStaticRule: invalid fullscreen state {}", effect); }
break;
}
case WINDOW_RULE_EFFECT_MOVE: {
static_.position = effect;
break;
}
case WINDOW_RULE_EFFECT_SIZE: {
static_.size = effect;
break;
}
case WINDOW_RULE_EFFECT_CENTER: {
static_.center = truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_PSEUDO: {
static_.pseudo = truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_MONITOR: {
static_.monitor = effect;
break;
}
case WINDOW_RULE_EFFECT_WORKSPACE: {
static_.workspace = effect;
break;
}
case WINDOW_RULE_EFFECT_NOINITIALFOCUS: {
static_.noInitialFocus = truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_PIN: {
static_.pin = truthy(effect);
break;
}
case WINDOW_RULE_EFFECT_GROUP: {
static_.group = effect;
break;
}
case WINDOW_RULE_EFFECT_SUPPRESSEVENT: {
CVarList2 varlist(std::string{effect}, 0, 's');
for (const auto& e : varlist) {
static_.suppressEvent.emplace_back(e);
}
break;
}
case WINDOW_RULE_EFFECT_CONTENT: {
static_.content = NContentType::fromString(effect);
break;
}
case WINDOW_RULE_EFFECT_NOCLOSEFOR: {
try {
static_.noCloseFor = std::stoi(effect);
} catch (...) { Debug::log(ERR, "CWindowRuleApplicator::applyStaticRule: invalid no close for {}", effect); }
break;
}
}
}
return SRuleResult{};
}
void CWindowRuleApplicator::readStaticRules() {
if (!m_window)
return;
static_ = {};
std::vector<SP<IRule>> toRemove;
bool tagsWereChanged = false;
for (const auto& r : ruleEngine()->rules()) {
if (r->type() != RULE_TYPE_WINDOW)
continue;
auto wr = reinterpretPointerCast<CWindowRule>(r);
if (!wr->matches(m_window.lock(), true))
continue;
applyStaticRule(wr);
const auto RES = applyDynamicRule(wr); // also apply dynamic, because we won't recheck it before layout gets data
tagsWereChanged = tagsWereChanged || RES.tagsChanged;
if (wr->isExecRule())
toRemove.emplace_back(wr);
}
for (const auto& wr : toRemove) {
ruleEngine()->unregisterRule(wr);
}
// recheck some props people might wanna use for static rules.
std::underlying_type_t<eRuleProperty> propsToRecheck = RULE_PROP_NONE;
if (tagsWereChanged)
propsToRecheck |= RULE_PROP_TAG;
if (static_.content != NContentType::CONTENT_TYPE_NONE)
propsToRecheck |= RULE_PROP_CONTENT;
if (propsToRecheck != RULE_PROP_NONE) {
for (const auto& r : ruleEngine()->rules()) {
if (r->type() != RULE_TYPE_WINDOW)
continue;
if (!(r->getPropertiesMask() & propsToRecheck))
continue;
auto wr = reinterpretPointerCast<CWindowRule>(r);
if (!wr->matches(m_window.lock(), true))
continue;
applyStaticRule(wr);
}
}
}
void CWindowRuleApplicator::propertiesChanged(std::underlying_type_t<eRuleProperty> props) {
if (!m_window || !m_window->m_isMapped || m_window->isHidden())
return;
resetProps(props);
bool needsRelayout = false;
std::unordered_set<CWindowRuleEffectContainer::storageType> effectsNeedingRecheck;
std::unordered_set<SP<CWindowRule>> passedWrs;
for (const auto& r : ruleEngine()->rules()) {
if (r->type() != RULE_TYPE_WINDOW)
continue;
if (!(r->getPropertiesMask() & props))
continue;
auto wr = reinterpretPointerCast<CWindowRule>(r);
if (!wr->matches(m_window.lock()))
continue;
for (const auto& [type, eff] : wr->effects()) {
effectsNeedingRecheck.emplace(type);
}
passedWrs.emplace(std::move(wr));
}
for (const auto& r : ruleEngine()->rules()) {
if (r->type() != RULE_TYPE_WINDOW)
continue;
const auto WR = reinterpretPointerCast<CWindowRule>(r);
if (!(WR->getPropertiesMask() & props) && !setsIntersect(WR->effectsSet(), effectsNeedingRecheck))
continue;
if (!std::ranges::contains(passedWrs, WR) && !WR->matches(m_window.lock()))
continue;
const auto RES = applyDynamicRule(WR);
needsRelayout = needsRelayout || RES.needsRelayout;
}
m_window->updateDecorationValues();
if (needsRelayout)
g_pDecorationPositioner->forceRecalcFor(m_window.lock());
// for plugins
EMIT_HOOK_EVENT("windowUpdateRules", m_window.lock());
}

View file

@ -0,0 +1,148 @@
#pragma once
#include <unordered_map>
#include "WindowRuleEffectContainer.hpp"
#include "../../DesktopTypes.hpp"
#include "../Rule.hpp"
#include "../../types/OverridableVar.hpp"
#include "../../../helpers/math/Math.hpp"
#include "../../../helpers/TagKeeper.hpp"
#include "../../../config/ConfigDataValues.hpp"
namespace Desktop::Rule {
class CWindowRule;
enum eIdleInhibitMode : uint8_t {
IDLEINHIBIT_NONE = 0,
IDLEINHIBIT_ALWAYS,
IDLEINHIBIT_FULLSCREEN,
IDLEINHIBIT_FOCUS
};
class CWindowRuleApplicator {
public:
CWindowRuleApplicator(PHLWINDOW w);
~CWindowRuleApplicator() = default;
CWindowRuleApplicator(const CWindowRuleApplicator&) = delete;
CWindowRuleApplicator(CWindowRuleApplicator&) = delete;
CWindowRuleApplicator(CWindowRuleApplicator&&) = delete;
void propertiesChanged(std::underlying_type_t<eRuleProperty> props);
void resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE);
void readStaticRules();
void applyStaticRules();
// static props
struct {
std::string monitor, workspace, group;
std::optional<bool> floating;
bool fullscreen = false;
bool maximize = false;
bool pseudo = false;
bool pin = false;
bool noInitialFocus = false;
std::optional<int> fullscreenStateClient;
std::optional<int> fullscreenStateInternal;
std::optional<int> center;
std::optional<int> content;
std::optional<int> noCloseFor;
std::string size, position;
std::vector<std::string> suppressEvent;
} static_;
struct SCustomPropContainer {
CWindowRuleEffectContainer::storageType idx = WINDOW_RULE_EFFECT_NONE;
std::underlying_type_t<eRuleProperty> propMask = RULE_PROP_NONE;
std::string effect;
};
// This struct holds props that were dynamically registered. Plugins may read this.
struct {
std::unordered_map<CWindowRuleEffectContainer::storageType, UP<SCustomPropContainer>> props;
} m_otherProps;
#define COMMA ,
#define DEFINE_PROP(type, name, def) \
private: \
std::pair<Types::COverridableVar<type>, std::underlying_type_t<eRuleProperty>> m_##name = {def, RULE_PROP_NONE}; \
\
public: \
Types::COverridableVar<type>& name() { \
return m_##name.first; \
} \
void name##Override(const Types::COverridableVar<type>& other) { \
m_##name.first = other; \
}
// dynamic props
DEFINE_PROP(Types::SAlphaValue, alpha, Types::SAlphaValue{})
DEFINE_PROP(Types::SAlphaValue, alphaInactive, Types::SAlphaValue{})
DEFINE_PROP(Types::SAlphaValue, alphaFullscreen, Types::SAlphaValue{})
DEFINE_PROP(bool, allowsInput, false)
DEFINE_PROP(bool, decorate, true)
DEFINE_PROP(bool, focusOnActivate, false)
DEFINE_PROP(bool, keepAspectRatio, false)
DEFINE_PROP(bool, nearestNeighbor, false)
DEFINE_PROP(bool, noAnim, false)
DEFINE_PROP(bool, noBlur, false)
DEFINE_PROP(bool, noDim, false)
DEFINE_PROP(bool, noFocus, false)
DEFINE_PROP(bool, noMaxSize, false)
DEFINE_PROP(bool, noShadow, false)
DEFINE_PROP(bool, noShortcutsInhibit, false)
DEFINE_PROP(bool, opaque, false)
DEFINE_PROP(bool, dimAround, false)
DEFINE_PROP(bool, RGBX, false)
DEFINE_PROP(bool, syncFullscreen, true)
DEFINE_PROP(bool, tearing, false)
DEFINE_PROP(bool, xray, false)
DEFINE_PROP(bool, renderUnfocused, false)
DEFINE_PROP(bool, noFollowMouse, false)
DEFINE_PROP(bool, noScreenShare, false)
DEFINE_PROP(bool, noVRR, false)
DEFINE_PROP(bool, persistentSize, false)
DEFINE_PROP(bool, stayFocused, false)
DEFINE_PROP(int, idleInhibitMode, false)
DEFINE_PROP(Hyprlang::INT, borderSize, {std::string("general:border_size") COMMA sc<Hyprlang::INT>(0) COMMA std::nullopt})
DEFINE_PROP(Hyprlang::INT, rounding, {std::string("decoration:rounding") COMMA sc<Hyprlang::INT>(0) COMMA std::nullopt})
DEFINE_PROP(Hyprlang::FLOAT, roundingPower, {std::string("decoration:rounding_power")})
DEFINE_PROP(Hyprlang::FLOAT, scrollMouse, {std::string("input:scroll_factor")})
DEFINE_PROP(Hyprlang::FLOAT, scrollTouchpad, {std::string("input:touchpad:scroll_factor")})
DEFINE_PROP(std::string, animationStyle, std::string(""))
DEFINE_PROP(Vector2D, maxSize, Vector2D{})
DEFINE_PROP(Vector2D, minSize, Vector2D{})
DEFINE_PROP(CGradientValueData, activeBorderColor, {})
DEFINE_PROP(CGradientValueData, inactiveBorderColor, {})
std::vector<std::pair<std::string, std::underlying_type_t<eRuleProperty>>> m_dynamicTags;
CTagKeeper m_tagKeeper;
#undef COMMA
#undef DEFINE_PROP
private:
PHLWINDOWREF m_window;
struct SRuleResult {
bool needsRelayout = false;
bool tagsChanged = false;
};
SRuleResult applyDynamicRule(const SP<CWindowRule>& rule);
SRuleResult applyStaticRule(const SP<CWindowRule>& rule);
};
};

View file

@ -0,0 +1,76 @@
#include "WindowRuleEffectContainer.hpp"
using namespace Desktop;
using namespace Desktop::Rule;
//
SP<CWindowRuleEffectContainer> Rule::windowEffects() {
static SP<CWindowRuleEffectContainer> container = makeShared<CWindowRuleEffectContainer>();
return container;
}
static const std::vector<std::string> EFFECT_STRINGS = {
"__internal_none", //
"float", //
"tile", //
"fullscreen", //
"maximize", //
"fullscreen_state", //
"move", //
"size", //
"center", //
"pseudo", //
"monitor", //
"workspace", //
"no_initial_focus", //
"pin", //
"group", //
"suppress_event", //
"content", //
"no_close_for", //
"rounding", //
"rounding_power", //
"persistent_size", //
"animation", //
"border_color", //
"idle_inhibit", //
"opacity", //
"tag", //
"max_size", //
"min_size", //
"border_size", //
"allows_input", //
"dim_around", //
"decorate", //
"focus_on_activate", //
"keep_aspect_ratio", //
"nearest_neighbor", //
"no_anim", //
"no_blur", //
"no_dim", //
"no_focus", //
"no_follow_mouse", //
"no_max_size", //
"no_shadow", //
"no_shortcuts_inhibit", //
"opaque", //
"force_rgbx", //
"sync_fullscreen", //
"immediate", //
"xray", //
"render_unfocused", //
"no_screen_share", //
"no_vrr", //
"scroll_mouse", //
"scroll_touchpad", //
"stay_focused", //
"__internal_last_static", //
};
// This is here so that if we change the rules, we get reminded to update
// the strings.
static_assert(WINDOW_RULE_EFFECT_LAST_STATIC == 54);
CWindowRuleEffectContainer::CWindowRuleEffectContainer() : IEffectContainer<eWindowRuleEffect>(std::vector<std::string>{EFFECT_STRINGS}) {
;
}

View file

@ -0,0 +1,79 @@
#pragma once
#include "../effect/EffectContainer.hpp"
#include "../../../helpers/memory/Memory.hpp"
#pragma once
namespace Desktop::Rule {
enum eWindowRuleEffect : uint8_t {
WINDOW_RULE_EFFECT_NONE = 0,
// static
WINDOW_RULE_EFFECT_FLOAT,
WINDOW_RULE_EFFECT_TILE,
WINDOW_RULE_EFFECT_FULLSCREEN,
WINDOW_RULE_EFFECT_MAXIMIZE,
WINDOW_RULE_EFFECT_FULLSCREENSTATE,
WINDOW_RULE_EFFECT_MOVE,
WINDOW_RULE_EFFECT_SIZE,
WINDOW_RULE_EFFECT_CENTER,
WINDOW_RULE_EFFECT_PSEUDO,
WINDOW_RULE_EFFECT_MONITOR,
WINDOW_RULE_EFFECT_WORKSPACE,
WINDOW_RULE_EFFECT_NOINITIALFOCUS,
WINDOW_RULE_EFFECT_PIN,
WINDOW_RULE_EFFECT_GROUP,
WINDOW_RULE_EFFECT_SUPPRESSEVENT,
WINDOW_RULE_EFFECT_CONTENT,
WINDOW_RULE_EFFECT_NOCLOSEFOR,
// dynamic
WINDOW_RULE_EFFECT_ROUNDING,
WINDOW_RULE_EFFECT_ROUNDING_POWER,
WINDOW_RULE_EFFECT_PERSISTENT_SIZE,
WINDOW_RULE_EFFECT_ANIMATION,
WINDOW_RULE_EFFECT_BORDER_COLOR,
WINDOW_RULE_EFFECT_IDLE_INHIBIT,
WINDOW_RULE_EFFECT_OPACITY,
WINDOW_RULE_EFFECT_TAG,
WINDOW_RULE_EFFECT_MAX_SIZE,
WINDOW_RULE_EFFECT_MIN_SIZE,
WINDOW_RULE_EFFECT_BORDER_SIZE,
WINDOW_RULE_EFFECT_ALLOWS_INPUT,
WINDOW_RULE_EFFECT_DIM_AROUND,
WINDOW_RULE_EFFECT_DECORATE,
WINDOW_RULE_EFFECT_FOCUS_ON_ACTIVATE,
WINDOW_RULE_EFFECT_KEEP_ASPECT_RATIO,
WINDOW_RULE_EFFECT_NEAREST_NEIGHBOR,
WINDOW_RULE_EFFECT_NO_ANIM,
WINDOW_RULE_EFFECT_NO_BLUR,
WINDOW_RULE_EFFECT_NO_DIM,
WINDOW_RULE_EFFECT_NO_FOCUS,
WINDOW_RULE_EFFECT_NO_FOLLOW_MOUSE,
WINDOW_RULE_EFFECT_NO_MAX_SIZE,
WINDOW_RULE_EFFECT_NO_SHADOW,
WINDOW_RULE_EFFECT_NO_SHORTCUTS_INHIBIT,
WINDOW_RULE_EFFECT_OPAQUE,
WINDOW_RULE_EFFECT_FORCE_RGBX,
WINDOW_RULE_EFFECT_SYNC_FULLSCREEN,
WINDOW_RULE_EFFECT_IMMEDIATE,
WINDOW_RULE_EFFECT_XRAY,
WINDOW_RULE_EFFECT_RENDER_UNFOCUSED,
WINDOW_RULE_EFFECT_NO_SCREEN_SHARE,
WINDOW_RULE_EFFECT_NO_VRR,
WINDOW_RULE_EFFECT_SCROLL_MOUSE,
WINDOW_RULE_EFFECT_SCROLL_TOUCHPAD,
WINDOW_RULE_EFFECT_STAY_FOCUSED,
WINDOW_RULE_EFFECT_LAST_STATIC,
};
class CWindowRuleEffectContainer : public IEffectContainer<eWindowRuleEffect> {
public:
CWindowRuleEffectContainer();
virtual ~CWindowRuleEffectContainer() = default;
};
SP<CWindowRuleEffectContainer> windowEffects();
};

View file

@ -0,0 +1,153 @@
#pragma once
#include <cstdint>
#include <type_traits>
#include <any>
#include <map>
#include "../../config/ConfigValue.hpp"
namespace Desktop::Types {
struct SAlphaValue {
float alpha = 1.F;
bool overridden = false;
float applyAlpha(float a) const {
if (overridden)
return alpha;
else
return alpha * a;
};
};
enum eOverridePriority : uint8_t {
PRIORITY_LAYOUT = 0,
PRIORITY_WORKSPACE_RULE,
PRIORITY_WINDOW_RULE,
PRIORITY_SET_PROP,
};
template <typename T>
T clampOptional(T const& value, std::optional<T> const& min, std::optional<T> const& max) {
return std::clamp(value, min.value_or(std::numeric_limits<T>::min()), max.value_or(std::numeric_limits<T>::max()));
}
template <typename T, bool Extended = std::is_same_v<T, bool> || std::is_same_v<T, Hyprlang::INT> || std::is_same_v<T, Hyprlang::FLOAT>>
class COverridableVar {
public:
COverridableVar(T const& value, eOverridePriority priority) {
m_values[priority] = value;
}
COverridableVar(T const& value) : m_defaultValue{value} {}
COverridableVar(T const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt) : m_defaultValue{value}, m_minValue{min}, m_maxValue{max} {}
COverridableVar(std::string const& value)
requires(Extended && !std::is_same_v<T, bool>)
: m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
COverridableVar(std::string const& value, std::optional<T> const& min, std::optional<T> const& max = std::nullopt)
requires(Extended && !std::is_same_v<T, bool>)
: m_minValue(min), m_maxValue(max), m_configValue(SP<CConfigValue<T>>(new CConfigValue<T>(value))) {}
COverridableVar() = default;
~COverridableVar() = default;
COverridableVar& operator=(COverridableVar<T> const& other) {
// Self-assignment check
if (this == &other)
return *this;
for (auto const& value : other.m_values) {
if constexpr (Extended && !std::is_same_v<T, bool>)
m_values[value.first] = clampOptional(value.second, m_minValue, m_maxValue);
else
m_values[value.first] = value.second;
}
return *this;
}
void set(T value, eOverridePriority priority) {
m_values[priority] = value;
}
void unset(eOverridePriority priority) {
m_values.erase(priority);
}
bool hasValue() const {
return !m_values.empty();
}
T value() const {
if (!m_values.empty())
return std::prev(m_values.end())->second;
else
throw std::bad_optional_access();
}
T valueOr(T const& other) const {
if (hasValue())
return value();
else
return other;
}
T valueOrDefault() const
requires(Extended && !std::is_same_v<T, bool>)
{
if (hasValue())
return value();
else if (m_defaultValue.has_value())
return m_defaultValue.value();
else
return **std::any_cast<SP<CConfigValue<T>>>(m_configValue);
}
T valueOrDefault() const
requires(!Extended || std::is_same_v<T, bool>)
{
if (hasValue())
return value();
else if (!m_defaultValue.has_value())
throw std::bad_optional_access();
else
return m_defaultValue.value();
}
eOverridePriority getPriority() const {
if (!m_values.empty())
return std::prev(m_values.end())->first;
else
throw std::bad_optional_access();
}
void increment(T const& other, eOverridePriority priority) {
if constexpr (std::is_same_v<T, bool>)
m_values[priority] = valueOr(false) ^ other;
else
m_values[priority] = clampOptional(valueOrDefault() + other, m_minValue, m_maxValue);
}
void matchOptional(std::optional<T> const& optValue, eOverridePriority priority) {
if (optValue.has_value())
m_values[priority] = optValue.value();
else
unset(priority);
}
operator std::optional<T>() {
if (hasValue())
return value();
else
return std::nullopt;
}
private:
std::map<eOverridePriority, T> m_values;
std::optional<T> m_defaultValue; // used for toggling, so required for bool
std::optional<T> m_minValue;
std::optional<T> m_maxValue;
std::any m_configValue; // only there for select variables
};
}

View file

@ -135,31 +135,23 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_X11ShouldntFocus = PWINDOW->m_X11ShouldntFocus || (PWINDOW->m_isX11 && PWINDOW->isX11OverrideRedirect() && !PWINDOW->m_xwaylandSurface->wantsFocus()); PWINDOW->m_X11ShouldntFocus = PWINDOW->m_X11ShouldntFocus || (PWINDOW->m_isX11 && PWINDOW->isX11OverrideRedirect() && !PWINDOW->m_xwaylandSurface->wantsFocus());
// window rules // window rules
PWINDOW->m_matchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false);
std::optional<eFullscreenMode> requestedInternalFSMode, requestedClientFSMode; std::optional<eFullscreenMode> requestedInternalFSMode, requestedClientFSMode;
std::optional<SFullscreenState> requestedFSState; std::optional<SFullscreenState> requestedFSState;
if (PWINDOW->m_wantsInitialFullscreen || (PWINDOW->m_isX11 && PWINDOW->m_xwaylandSurface->m_fullscreen)) if (PWINDOW->m_wantsInitialFullscreen || (PWINDOW->m_isX11 && PWINDOW->m_xwaylandSurface->m_fullscreen))
requestedClientFSMode = FSMODE_FULLSCREEN; requestedClientFSMode = FSMODE_FULLSCREEN;
MONITORID requestedFSMonitor = PWINDOW->m_wantsInitialFullscreenMonitor; MONITORID requestedFSMonitor = PWINDOW->m_wantsInitialFullscreenMonitor;
for (auto const& r : PWINDOW->m_matchedRules) { PWINDOW->m_ruleApplicator->readStaticRules();
switch (r->m_ruleType) { {
case CWindowRule::RULE_MONITOR: { if (!PWINDOW->m_ruleApplicator->static_.monitor.empty()) {
try { const auto& MONITORSTR = PWINDOW->m_ruleApplicator->static_.monitor;
const auto MONITORSTR = trim(r->m_rule.substr(r->m_rule.find(' ')));
if (MONITORSTR == "unset") if (MONITORSTR == "unset")
PWINDOW->m_monitor = PMONITOR; PWINDOW->m_monitor = PMONITOR;
else { else {
const auto MONITOR = g_pCompositor->getMonitorFromString(MONITORSTR); const auto MONITOR = g_pCompositor->getMonitorFromString(MONITORSTR);
if (MONITOR) if (MONITOR) {
PWINDOW->m_monitor = MONITOR; PWINDOW->m_monitor = MONITOR;
else {
Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR);
continue;
}
}
const auto PMONITORFROMID = PWINDOW->m_monitor.lock(); const auto PMONITORFROMID = PWINDOW->m_monitor.lock();
@ -172,12 +164,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW); Debug::log(LOG, "Rule monitor, applying to {:mw}", PWINDOW);
requestedFSMonitor = MONITOR_INVALID; requestedFSMonitor = MONITOR_INVALID;
} catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: {} -> {} | err: {}", r->m_rule, r->m_value, e.what()); } } else
break; Debug::log(ERR, "No monitor in monitor {} rule", MONITORSTR);
} }
case CWindowRule::RULE_WORKSPACE: { }
// check if it isn't unset
const auto WORKSPACERQ = r->m_rule.substr(r->m_rule.find_first_of(' ') + 1); if (!PWINDOW->m_ruleApplicator->static_.workspace.empty()) {
const auto WORKSPACERQ = PWINDOW->m_ruleApplicator->static_.workspace;
if (WORKSPACERQ == "unset") if (WORKSPACERQ == "unset")
requestedWorkspace = ""; requestedWorkspace = "";
@ -189,83 +182,55 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (JUSTWORKSPACE == PWORKSPACE->m_name || JUSTWORKSPACE == "name:" + PWORKSPACE->m_name) if (JUSTWORKSPACE == PWORKSPACE->m_name || JUSTWORKSPACE == "name:" + PWORKSPACE->m_name)
requestedWorkspace = ""; requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, r->m_value); Debug::log(LOG, "Rule workspace matched by {}, {} applied.", PWINDOW, PWINDOW->m_ruleApplicator->static_.workspace);
requestedFSMonitor = MONITOR_INVALID; requestedFSMonitor = MONITOR_INVALID;
break;
} }
case CWindowRule::RULE_FLOAT: {
PWINDOW->m_isFloating = true; if (PWINDOW->m_ruleApplicator->static_.floating.has_value())
break; PWINDOW->m_isFloating = PWINDOW->m_ruleApplicator->static_.floating.value();
}
case CWindowRule::RULE_TILE: { if (PWINDOW->m_ruleApplicator->static_.pseudo)
PWINDOW->m_isFloating = false;
break;
}
case CWindowRule::RULE_PSEUDO: {
PWINDOW->m_isPseudotiled = true; PWINDOW->m_isPseudotiled = true;
break;
} if (PWINDOW->m_ruleApplicator->static_.noInitialFocus)
case CWindowRule::RULE_NOINITIALFOCUS: {
PWINDOW->m_noInitialFocus = true; PWINDOW->m_noInitialFocus = true;
break;
if (PWINDOW->m_ruleApplicator->static_.fullscreenStateClient || PWINDOW->m_ruleApplicator->static_.fullscreenStateInternal) {
requestedFSState = SFullscreenState{
.internal = sc<eFullscreenMode>(PWINDOW->m_ruleApplicator->static_.fullscreenStateInternal.value_or(0)),
.client = sc<eFullscreenMode>(PWINDOW->m_ruleApplicator->static_.fullscreenStateClient.value_or(0)),
};
} }
case CWindowRule::RULE_FULLSCREENSTATE: {
const auto ARGS = CVarList(r->m_rule.substr(r->m_rule.find_first_of(' ') + 1), 2, ' '); if (!PWINDOW->m_ruleApplicator->static_.suppressEvent.empty()) {
int internalMode, clientMode; for (const auto& var : PWINDOW->m_ruleApplicator->static_.suppressEvent) {
try { if (var == "fullscreen")
internalMode = std::stoi(ARGS[0]);
} catch (std::exception& e) { internalMode = 0; }
try {
clientMode = std::stoi(ARGS[1]);
} catch (std::exception& e) { clientMode = 0; }
requestedFSState = SFullscreenState{.internal = sc<eFullscreenMode>(internalMode), .client = sc<eFullscreenMode>(clientMode)};
break;
}
case CWindowRule::RULE_SUPPRESSEVENT: {
CVarList vars(r->m_rule, 0, 's', true);
for (size_t i = 1; i < vars.size(); ++i) {
if (vars[i] == "fullscreen")
PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN; PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN;
else if (vars[i] == "maximize") else if (var == "maximize")
PWINDOW->m_suppressedEvents |= SUPPRESS_MAXIMIZE; PWINDOW->m_suppressedEvents |= SUPPRESS_MAXIMIZE;
else if (vars[i] == "activate") else if (var == "activate")
PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE; PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE;
else if (vars[i] == "activatefocus") else if (var == "activatefocus")
PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY; PWINDOW->m_suppressedEvents |= SUPPRESS_ACTIVATE_FOCUSONLY;
else if (vars[i] == "fullscreenoutput") else if (var == "fullscreenoutput")
PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN_OUTPUT; PWINDOW->m_suppressedEvents |= SUPPRESS_FULLSCREEN_OUTPUT;
else else
Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", vars[i]); Debug::log(ERR, "Error while parsing suppressevent windowrule: unknown event type {}", var);
} }
break;
} }
case CWindowRule::RULE_PIN: {
if (PWINDOW->m_ruleApplicator->static_.pin)
PWINDOW->m_pinned = true; PWINDOW->m_pinned = true;
break;
} if (PWINDOW->m_ruleApplicator->static_.fullscreen)
case CWindowRule::RULE_FULLSCREEN: {
requestedInternalFSMode = FSMODE_FULLSCREEN; requestedInternalFSMode = FSMODE_FULLSCREEN;
break;
} if (PWINDOW->m_ruleApplicator->static_.maximize)
case CWindowRule::RULE_MAXIMIZE: {
requestedInternalFSMode = FSMODE_MAXIMIZED; requestedInternalFSMode = FSMODE_MAXIMIZED;
break;
}
case CWindowRule::RULE_STAYFOCUSED: {
PWINDOW->m_stayFocused = true;
break;
}
case CWindowRule::RULE_GROUP: {
if (PWINDOW->m_groupRules & GROUP_OVERRIDE)
continue;
// `group` is a shorthand of `group set` if (!PWINDOW->m_ruleApplicator->static_.group.empty()) {
if (trim(r->m_rule) == "group") { if (!(PWINDOW->m_groupRules & GROUP_OVERRIDE) && trim(PWINDOW->m_ruleApplicator->static_.group) != "group") {
PWINDOW->m_groupRules |= GROUP_SET; CVarList2 vars(std::string{PWINDOW->m_ruleApplicator->static_.group}, 0, 's');
continue;
}
CVarList vars(r->m_rule, 0, 's');
std::string vPrev = ""; std::string vPrev = "";
for (auto const& v : vars) { for (auto const& v : vars) {
@ -302,26 +267,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
} }
vPrev = v; vPrev = v;
} }
break;
} }
case CWindowRule::RULE_CONTENT: {
const CVarList VARS(r->m_rule, 0, ' ');
try {
PWINDOW->setContentType(NContentType::fromString(VARS[1]));
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
break;
}
case CWindowRule::RULE_NOCLOSEFOR: {
const CVarList VARS(r->m_rule, 0, ' ');
try {
PWINDOW->m_closeableSince = Time::steadyNow() + std::chrono::milliseconds(std::stoull(VARS[1]));
} catch (std::exception& e) { Debug::log(ERR, "Rule \"{}\" failed with: {}", r->m_rule, e.what()); }
break;
}
default: break;
} }
PWINDOW->applyDynamicRule(r); if (PWINDOW->m_ruleApplicator->static_.content)
PWINDOW->setContentType(sc<NContentType::eContentType>(PWINDOW->m_ruleApplicator->static_.content.value()));
if (PWINDOW->m_ruleApplicator->static_.noCloseFor)
PWINDOW->m_closeableSince = Time::steadyNow() + std::chrono::milliseconds(PWINDOW->m_ruleApplicator->static_.noCloseFor.value());
} }
// make it uncloseable if it's a Hyprland dialog // make it uncloseable if it's a Hyprland dialog
@ -333,7 +286,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (PWINDOW->m_pinned && !PWINDOW->m_isFloating) if (PWINDOW->m_pinned && !PWINDOW->m_isFloating)
PWINDOW->m_pinned = false; PWINDOW->m_pinned = false;
CVarList WORKSPACEARGS = CVarList(requestedWorkspace, 0, ' '); CVarList2 WORKSPACEARGS = CVarList2(std::move(requestedWorkspace), 0, ' ', false, false);
if (!WORKSPACEARGS[0].empty()) { if (!WORKSPACEARGS[0].empty()) {
WORKSPACEID requestedWorkspaceID; WORKSPACEID requestedWorkspaceID;
@ -420,138 +373,29 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW); g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
PWINDOW->m_createdOverFullscreen = true; PWINDOW->m_createdOverFullscreen = true;
// size and move rules if (!PWINDOW->m_ruleApplicator->static_.size.empty()) {
for (auto const& r : PWINDOW->m_matchedRules) { const auto COMPUTED = PWINDOW->calculateExpression(PWINDOW->m_ruleApplicator->static_.size);
switch (r->m_ruleType) { if (!COMPUTED)
case CWindowRule::RULE_SIZE: { Debug::log(ERR, "failed to parse {} as an expression", PWINDOW->m_ruleApplicator->static_.size);
try { else {
auto stringToFloatClamp = [](const std::string& VALUE, const float CURR, const float REL) { *PWINDOW->m_realSize = *COMPUTED;
if (VALUE.starts_with('<'))
return std::min(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
else if (VALUE.starts_with('>'))
return std::max(CURR, stringToPercentage(VALUE.substr(1, VALUE.length() - 1), REL));
return stringToPercentage(VALUE, REL);
};
const auto VALUE = r->m_rule.substr(r->m_rule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const auto MAXSIZE = PWINDOW->requestedMaxSize();
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->m_size.x) :
stringToFloatClamp(SIZEXSTR, PWINDOW->m_realSize->goal().x, PMONITOR->m_size.x);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->m_size.y) :
stringToFloatClamp(SIZEYSTR, PWINDOW->m_realSize->goal().y, PMONITOR->m_size.y);
Debug::log(LOG, "Rule size, applying to {}", PWINDOW);
PWINDOW->clampWindowSize(Vector2D{SIZEXSTR.starts_with("<") ? 0 : SIZEX, SIZEYSTR.starts_with("<") ? 0 : SIZEY}, Vector2D{SIZEX, SIZEY});
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->m_rule, r->m_value); }
break;
}
case CWindowRule::RULE_MOVE: {
try {
auto value = r->m_rule.substr(r->m_rule.find(' ') + 1);
const bool ONSCREEN = value.starts_with("onscreen");
if (ONSCREEN)
value = value.substr(value.find_first_of(' ') + 1);
const bool CURSOR = value.starts_with("cursor");
if (CURSOR)
value = value.substr(value.find_first_of(' ') + 1);
const auto POSXSTR = value.substr(0, value.find(' '));
const auto POSYSTR = value.substr(value.find(' ') + 1);
int posX = 0;
int posY = 0;
if (POSXSTR.starts_with("100%-")) {
const bool subtractWindow = POSXSTR.starts_with("100%-w-");
const auto POSXRAW = (subtractWindow) ? POSXSTR.substr(7) : POSXSTR.substr(5);
posX =
PMONITOR->m_size.x - (!POSXRAW.contains('%') ? std::stoi(POSXRAW) : std::stof(POSXRAW.substr(0, POSXRAW.length() - 1)) * 0.01 * PMONITOR->m_size.x);
if (subtractWindow)
posX -= PWINDOW->m_realSize->goal().x;
if (CURSOR)
Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
} else if (!CURSOR) {
posX = !POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PMONITOR->m_size.x;
} else {
// cursor
if (POSXSTR == "cursor") {
posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->m_position.x;
} else {
posX = g_pInputManager->getMouseCoordsInternal().x - PMONITOR->m_position.x +
(!POSXSTR.contains('%') ? std::stoi(POSXSTR) : std::stof(POSXSTR.substr(0, POSXSTR.length() - 1)) * 0.01 * PWINDOW->m_realSize->goal().x);
} }
} }
if (POSYSTR.starts_with("100%-")) { if (!PWINDOW->m_ruleApplicator->static_.position.empty()) {
const bool subtractWindow = POSYSTR.starts_with("100%-h-"); const auto COMPUTED = PWINDOW->calculateExpression(PWINDOW->m_ruleApplicator->static_.position);
const auto POSYRAW = (subtractWindow) ? POSYSTR.substr(7) : POSYSTR.substr(5); if (!COMPUTED)
posY = Debug::log(ERR, "failed to parse {} as an expression", PWINDOW->m_ruleApplicator->static_.position);
PMONITOR->m_size.y - (!POSYRAW.contains('%') ? std::stoi(POSYRAW) : std::stof(POSYRAW.substr(0, POSYRAW.length() - 1)) * 0.01 * PMONITOR->m_size.y); else {
*PWINDOW->m_realPosition = *COMPUTED + PMONITOR->m_position;
if (subtractWindow)
posY -= PWINDOW->m_realSize->goal().y;
if (CURSOR)
Debug::log(ERR, "Cursor is not compatible with 100%-, ignoring cursor!");
} else if (!CURSOR) {
posY = !POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PMONITOR->m_size.y;
} else {
// cursor
if (POSYSTR == "cursor") {
posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->m_position.y;
} else {
posY = g_pInputManager->getMouseCoordsInternal().y - PMONITOR->m_position.y +
(!POSYSTR.contains('%') ? std::stoi(POSYSTR) : std::stof(POSYSTR.substr(0, POSYSTR.length() - 1)) * 0.01 * PWINDOW->m_realSize->goal().y);
}
}
if (ONSCREEN) {
int borderSize = PWINDOW->getRealBorderSize();
posX = std::clamp(posX, sc<int>(PMONITOR->m_reservedTopLeft.x + borderSize),
std::max(sc<int>(PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x - PWINDOW->m_realSize->goal().x - borderSize),
sc<int>(PMONITOR->m_reservedTopLeft.x + borderSize + 1)));
posY = std::clamp(posY, sc<int>(PMONITOR->m_reservedTopLeft.y + borderSize),
std::max(sc<int>(PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y - PWINDOW->m_realSize->goal().y - borderSize),
sc<int>(PMONITOR->m_reservedTopLeft.y + borderSize + 1)));
}
Debug::log(LOG, "Rule move, applying to {}", PWINDOW);
*PWINDOW->m_realPosition = Vector2D(posX, posY) + PMONITOR->m_position;
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule move failed, rule: {} -> {}", r->m_rule, r->m_value); }
break;
} }
case CWindowRule::RULE_CENTER: { }
auto RESERVEDOFFSET = Vector2D();
const auto ARGS = CVarList(r->m_rule, 2, ' ');
if (ARGS[1] == "1")
RESERVEDOFFSET = (PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight) / 2.f;
if (PWINDOW->m_ruleApplicator->static_.center) {
auto RESERVEDOFFSET = (PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight) / 2.f;
*PWINDOW->m_realPosition = PMONITOR->middle() - PWINDOW->m_realSize->goal() / 2.f + RESERVEDOFFSET; *PWINDOW->m_realPosition = PMONITOR->middle() - PWINDOW->m_realSize->goal() / 2.f + RESERVEDOFFSET;
break;
}
default: break;
}
} }
// set the pseudo size to the GOAL of our current size // set the pseudo size to the GOAL of our current size
@ -564,28 +408,15 @@ void Events::listener_mapWindow(void* owner, void* data) {
bool setPseudo = false; bool setPseudo = false;
for (auto const& r : PWINDOW->m_matchedRules) { if (!PWINDOW->m_ruleApplicator->static_.size.empty()) {
if (r->m_ruleType != CWindowRule::RULE_SIZE) const auto COMPUTED = PWINDOW->calculateExpression(PWINDOW->m_ruleApplicator->static_.size);
continue; if (!COMPUTED)
Debug::log(ERR, "failed to parse {} as an expression", PWINDOW->m_ruleApplicator->static_.size);
try { else {
const auto VALUE = r->m_rule.substr(r->m_rule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const auto MAXSIZE = PWINDOW->requestedMaxSize();
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, PMONITOR->m_size.x) : stringToPercentage(SIZEXSTR, PMONITOR->m_size.x);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, PMONITOR->m_size.y) : stringToPercentage(SIZEYSTR, PMONITOR->m_size.y);
Debug::log(LOG, "Rule size (tiled), applying to {}", PWINDOW);
setPseudo = true; setPseudo = true;
PWINDOW->m_pseudoSize = Vector2D(SIZEX, SIZEY); PWINDOW->m_pseudoSize = *COMPUTED;
PWINDOW->setHidden(false); PWINDOW->setHidden(false);
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->m_rule, r->m_value); } }
} }
if (!setPseudo) if (!setPseudo)
@ -594,8 +425,8 @@ void Events::listener_mapWindow(void* owner, void* data) {
const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_lastWindow.lock(); const auto PFOCUSEDWINDOWPREV = g_pCompositor->m_lastWindow.lock();
if (PWINDOW->m_windowData.allowsInput.valueOrDefault()) { // if default value wasn't set to false getPriority() would throw an exception if (PWINDOW->m_ruleApplicator->allowsInput().valueOrDefault()) { // if default value wasn't set to false getPriority() would throw an exception
PWINDOW->m_windowData.noFocus = CWindowOverridableVar(false, PWINDOW->m_windowData.allowsInput.getPriority()); PWINDOW->m_ruleApplicator->noFocusOverride(Desktop::Types::COverridableVar(false, PWINDOW->m_ruleApplicator->allowsInput().getPriority()));
PWINDOW->m_noInitialFocus = false; PWINDOW->m_noInitialFocus = false;
PWINDOW->m_X11ShouldntFocus = false; PWINDOW->m_X11ShouldntFocus = false;
} }
@ -615,12 +446,12 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pCompositor->setWindowFullscreenInternal(PWINDOW->m_workspace->getFullscreenWindow(), FSMODE_NONE); g_pCompositor->setWindowFullscreenInternal(PWINDOW->m_workspace->getFullscreenWindow(), FSMODE_NONE);
} }
if (!PWINDOW->m_windowData.noFocus.valueOrDefault() && !PWINDOW->m_noInitialFocus && if (!PWINDOW->m_ruleApplicator->noFocus().valueOrDefault() && !PWINDOW->m_noInitialFocus &&
(!PWINDOW->isX11OverrideRedirect() || (PWINDOW->m_isX11 && PWINDOW->m_xwaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) && (!PWINDOW->isX11OverrideRedirect() || (PWINDOW->m_isX11 && PWINDOW->m_xwaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) &&
!g_pInputManager->isConstrained()) { !g_pInputManager->isConstrained()) {
g_pCompositor->focusWindow(PWINDOW); g_pCompositor->focusWindow(PWINDOW);
PWINDOW->m_activeInactiveAlpha->setValueAndWarp(*PACTIVEALPHA); PWINDOW->m_activeInactiveAlpha->setValueAndWarp(*PACTIVEALPHA);
PWINDOW->m_dimPercent->setValueAndWarp(PWINDOW->m_windowData.noDim.valueOrDefault() ? 0.f : *PDIMSTRENGTH); PWINDOW->m_dimPercent->setValueAndWarp(PWINDOW->m_ruleApplicator->noDim().valueOrDefault() ? 0.f : *PDIMSTRENGTH);
} else { } else {
PWINDOW->m_activeInactiveAlpha->setValueAndWarp(*PINACTIVEALPHA); PWINDOW->m_activeInactiveAlpha->setValueAndWarp(*PINACTIVEALPHA);
PWINDOW->m_dimPercent->setValueAndWarp(0); PWINDOW->m_dimPercent->setValueAndWarp(0);
@ -639,9 +470,9 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_realPosition->warp(); PWINDOW->m_realPosition->warp();
PWINDOW->m_realSize->warp(); PWINDOW->m_realSize->warp();
if (requestedFSState.has_value()) { if (requestedFSState.has_value()) {
PWINDOW->m_windowData.syncFullscreen = CWindowOverridableVar(false, PRIORITY_WINDOW_RULE); PWINDOW->m_ruleApplicator->syncFullscreenOverride(Desktop::Types::COverridableVar(false, Desktop::Types::PRIORITY_WINDOW_RULE));
g_pCompositor->setWindowFullscreenState(PWINDOW, requestedFSState.value()); g_pCompositor->setWindowFullscreenState(PWINDOW, requestedFSState.value());
} else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_windowData.syncFullscreen.valueOrDefault()) } else if (requestedInternalFSMode.has_value() && requestedClientFSMode.has_value() && !PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()}); g_pCompositor->setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = requestedInternalFSMode.value(), .client = requestedClientFSMode.value()});
else if (requestedInternalFSMode.has_value()) else if (requestedInternalFSMode.has_value())
g_pCompositor->setWindowFullscreenInternal(PWINDOW, requestedInternalFSMode.value()); g_pCompositor->setWindowFullscreenInternal(PWINDOW, requestedInternalFSMode.value());
@ -653,6 +484,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
g_pInputManager->recheckIdleInhibitorStatus(); g_pInputManager->recheckIdleInhibitorStatus();
PWINDOW->updateToplevel(); PWINDOW->updateToplevel();
PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL);
if (workspaceSilent) { if (workspaceSilent) {
if (validMapped(PFOCUSEDWINDOWPREV)) { if (validMapped(PFOCUSEDWINDOWPREV)) {
@ -689,7 +521,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_realSize->setCallbackOnEnd(setVector2DAnimToMove); PWINDOW->m_realSize->setCallbackOnEnd(setVector2DAnimToMove);
// recalc the values for this window // recalc the values for this window
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); PWINDOW->updateDecorationValues();
// avoid this window being visible // avoid this window being visible
if (PWORKSPACE->m_hasFullscreenWindow && !PWINDOW->isFullscreen() && !PWINDOW->m_isFloating) if (PWORKSPACE->m_hasFullscreenWindow && !PWINDOW->isFullscreen() && !PWINDOW->m_isFloating)
PWINDOW->m_alpha->setValueAndWarp(0.f); PWINDOW->m_alpha->setValueAndWarp(0.f);
@ -736,8 +568,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)}); g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", std::format("{:x}", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW); EMIT_HOOK_EVENT("closeWindow", PWINDOW);
if (PWINDOW->m_isFloating && !PWINDOW->m_isX11 && if (PWINDOW->m_isFloating && !PWINDOW->m_isX11 && PWINDOW->m_ruleApplicator->persistentSize().valueOrDefault()) {
std::ranges::any_of(PWINDOW->m_matchedRules, [](const auto& r) { return r->m_ruleType == CWindowRule::RULE_PERSISTENTSIZE; })) {
Debug::log(LOG, "storing floating size {}x{} for window {}::{} on close", PWINDOW->m_realSize->value().x, PWINDOW->m_realSize->value().y, PWINDOW->m_class, Debug::log(LOG, "storing floating size {}x{} for window {}::{} on close", PWINDOW->m_realSize->value().x, PWINDOW->m_realSize->value().y, PWINDOW->m_class,
PWINDOW->m_title); PWINDOW->m_title);
g_pConfigManager->storeFloatingSize(PWINDOW, PWINDOW->m_realSize->value()); g_pConfigManager->storeFloatingSize(PWINDOW, PWINDOW->m_realSize->value());

View file

@ -975,3 +975,13 @@ std::string getBuiltSystemLibraryNames() {
result += std::format("Aquamarine: built against {}, system has {}\n", AQUAMARINE_VERSION, getSystemLibraryVersion("aquamarine")); result += std::format("Aquamarine: built against {}, system has {}\n", AQUAMARINE_VERSION, getSystemLibraryVersion("aquamarine"));
return result; return result;
} }
bool truthy(const std::string& str) {
if (str == "1")
return true;
std::string cpy = str;
std::ranges::transform(cpy, cpy.begin(), ::tolower);
return cpy.starts_with("true") || cpy.starts_with("yes") || cpy.starts_with("on");
}

View file

@ -46,6 +46,7 @@ std::expected<std::string, std::string> binaryNameForPid(pid_t pid);
std::string deviceNameToInternalString(std::string in); std::string deviceNameToInternalString(std::string in);
std::string getSystemLibraryVersion(const std::string& name); std::string getSystemLibraryVersion(const std::string& name);
std::string getBuiltSystemLibraryNames(); std::string getBuiltSystemLibraryNames();
bool truthy(const std::string& str);
template <typename... Args> template <typename... Args>
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) { [[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {

View file

@ -27,6 +27,7 @@
#include "../managers/animation/DesktopAnimationManager.hpp" #include "../managers/animation/DesktopAnimationManager.hpp"
#include "../managers/input/InputManager.hpp" #include "../managers/input/InputManager.hpp"
#include "../hyprerror/HyprError.hpp" #include "../hyprerror/HyprError.hpp"
#include "../i18n/Engine.hpp"
#include "sync/SyncTimeline.hpp" #include "sync/SyncTimeline.hpp"
#include "time/Time.hpp" #include "time/Time.hpp"
#include "../desktop/LayerSurface.hpp" #include "../desktop/LayerSurface.hpp"
@ -176,6 +177,10 @@ void CMonitor::onConnect(bool noRule) {
m_forceSize = SIZE; m_forceSize = SIZE;
SMonitorRule rule = m_activeMonitorRule; SMonitorRule rule = m_activeMonitorRule;
if (SIZE == rule.resolution)
return;
rule.resolution = SIZE; rule.resolution = SIZE;
applyMonitorRule(&rule); applyMonitorRule(&rule);
@ -811,8 +816,8 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
if (!m_state.test()) if (!m_state.test())
continue; continue;
auto errorMessage = auto errorMessage = I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_MODE_FAIL,
std::format("Monitor {} failed to set any requested modes, falling back to mode {:X0}@{:.2f}Hz", m_name, mode->pixelSize, mode->refreshRate / 1000.f); {{"name", m_name}, {"mode", std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f)}});
Debug::log(WARN, errorMessage); Debug::log(WARN, errorMessage);
g_pHyprNotificationOverlay->addNotification(errorMessage, CHyprColor(0xff0000ff), 5000, ICON_WARNING); g_pHyprNotificationOverlay->addNotification(errorMessage, CHyprColor(0xff0000ff), 5000, ICON_WARNING);
@ -939,7 +944,9 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) {
Debug::log(ERR, "Invalid scale passed to monitor, {} found suggestion {}", m_scale, searchScale); Debug::log(ERR, "Invalid scale passed to monitor, {} found suggestion {}", m_scale, searchScale);
static auto PDISABLENOTIFICATION = CConfigValue<Hyprlang::INT>("misc:disable_scale_notification"); static auto PDISABLENOTIFICATION = CConfigValue<Hyprlang::INT>("misc:disable_scale_notification");
if (!*PDISABLENOTIFICATION) if (!*PDISABLENOTIFICATION)
g_pHyprNotificationOverlay->addNotification(std::format("Invalid scale passed to monitor: {}, using suggested scale: {}", m_scale, searchScale), g_pHyprNotificationOverlay->addNotification(
I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
{{"name", m_name}, {"scale", std::format("{:.2f}", m_scale)}, {"fixed_scale", std::format("{:.2f}", searchScale)}}),
CHyprColor(1.0, 0.0, 0.0, 1.0), 5000, ICON_WARNING); CHyprColor(1.0, 0.0, 0.0, 1.0), 5000, ICON_WARNING);
} }
m_scale = searchScale; m_scale = searchScale;
@ -1509,6 +1516,10 @@ CBox CMonitor::logicalBox() {
return {m_position, m_size}; return {m_position, m_size};
} }
CBox CMonitor::logicalBoxMinusExtents() {
return {m_position + m_reservedTopLeft, m_size - m_reservedTopLeft - m_reservedBottomRight};
}
void CMonitor::scheduleDone() { void CMonitor::scheduleDone() {
if (m_doneScheduled) if (m_doneScheduled)
return; return;

View file

@ -298,6 +298,7 @@ class CMonitor {
WORKSPACEID activeWorkspaceID(); WORKSPACEID activeWorkspaceID();
WORKSPACEID activeSpecialWorkspaceID(); WORKSPACEID activeSpecialWorkspaceID();
CBox logicalBox(); CBox logicalBox();
CBox logicalBoxMinusExtents();
void scheduleDone(); void scheduleDone();
uint32_t isSolitaryBlocked(bool full = false); uint32_t isSolitaryBlocked(bool full = false);
void recheckSolitary(); void recheckSolitary();

View file

@ -1,6 +1,6 @@
#include "TagKeeper.hpp" #include "TagKeeper.hpp"
bool CTagKeeper::isTagged(const std::string& tag, bool strict) { bool CTagKeeper::isTagged(const std::string& tag, bool strict) const {
const bool NEGATIVE = tag.starts_with("negative"); const bool NEGATIVE = tag.starts_with("negative");
const auto MATCH = NEGATIVE ? tag.substr(9) : tag; const auto MATCH = NEGATIVE ? tag.substr(9) : tag;
const bool TAGGED = m_tags.contains(MATCH) || (!strict && m_tags.contains(MATCH + "*")); const bool TAGGED = m_tags.contains(MATCH) || (!strict && m_tags.contains(MATCH + "*"));
@ -38,6 +38,6 @@ bool CTagKeeper::applyTag(const std::string& tag, bool dynamic) {
return true; return true;
} }
bool CTagKeeper::removeDynamicTags() { bool CTagKeeper::removeDynamicTag(const std::string& s) {
return std::erase_if(m_tags, [](const auto& tag) { return tag.ends_with("*"); }); return std::erase_if(m_tags, [&s](const auto& tag) { return tag == s + "*"; });
} }

View file

@ -5,9 +5,9 @@
class CTagKeeper { class CTagKeeper {
public: public:
bool isTagged(const std::string& tag, bool strict = false); bool isTagged(const std::string& tag, bool strict = false) const;
bool applyTag(const std::string& tag, bool dynamic = false); bool applyTag(const std::string& tag, bool dynamic = false);
bool removeDynamicTags(); bool removeDynamicTag(const std::string& tag);
const auto& getTags() const { const auto& getTags() const {
return m_tags; return m_tags;

View file

@ -0,0 +1,22 @@
#include "Expression.hpp"
#include "muParser.h"
#include "../../debug/Log.hpp"
using namespace Math;
CExpression::CExpression() : m_parser(makeUnique<mu::Parser>()) {
;
}
void CExpression::addVariable(const std::string& name, double val) {
m_parser->DefineConst(name, val);
}
std::optional<double> CExpression::compute(const std::string& expr) {
try {
m_parser->SetExpr(expr);
return m_parser->Eval();
} catch (mu::Parser::exception_type& e) { Debug::log(ERR, "CExpression::compute: mu threw: {}", e.GetMsg()); }
return std::nullopt;
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "../memory/Memory.hpp"
#include <string>
#include <optional>
namespace mu {
class Parser;
};
namespace Math {
class CExpression {
public:
CExpression();
~CExpression() = default;
CExpression(const CExpression&) = delete;
CExpression(CExpression&) = delete;
CExpression(CExpression&&) = delete;
void addVariable(const std::string& name, double val);
std::optional<double> compute(const std::string& expr);
private:
UP<mu::Parser> m_parser;
};
};

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <hyprutils/string/VarList.hpp> #include <hyprutils/string/VarList.hpp>
#include <hyprutils/string/VarList2.hpp>
//NOLINTNEXTLINE //NOLINTNEXTLINE
using namespace Hyprutils::String; using namespace Hyprutils::String;

988
src/i18n/Engine.cpp Normal file
View file

@ -0,0 +1,988 @@
#include "Engine.hpp"
#include <hyprutils/i18n/I18nEngine.hpp>
using namespace I18n;
using namespace Hyprutils::I18n;
static SP<Hyprutils::I18n::CI18nEngine> huEngine;
static std::string localeStr;
//
SP<I18n::CI18nEngine> I18n::i18nEngine() {
static SP<I18n::CI18nEngine> engine = makeShared<I18n::CI18nEngine>();
return engine;
}
I18n::CI18nEngine::CI18nEngine() {
huEngine = makeShared<Hyprutils::I18n::CI18nEngine>();
huEngine->setFallbackLocale("en_US");
localeStr = huEngine->getSystemLocale().locale();
// be_BY (Belarusian)
huEngine->registerEntry("be_BY", TXT_KEY_ANR_TITLE, "Праграма не адказвае");
huEngine->registerEntry("be_BY", TXT_KEY_ANR_CONTENT, "Праграма {title} - {class} не адказвае.\nШто хочаце з ёй зрабіць?");
huEngine->registerEntry("be_BY", TXT_KEY_ANR_OPTION_TERMINATE, "Прымусова спыніць");
huEngine->registerEntry("be_BY", TXT_KEY_ANR_OPTION_WAIT, "Пачакаць");
huEngine->registerEntry("be_BY", TXT_KEY_ANR_PROP_UNKNOWN, "(невядома)");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Праграма <b>{app}</b> запытвае невядомы дазвол.");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Праграма <b>{app}</b> спрабуе здымаць экран.\n\nЦі хочаце дазволіць?");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Праграма <b>{app}</b> спрабуе загрузіць плагін: <b>{plugin}</b>.\n\nХочаце дазволіць?");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Выяўленая новая клавіятура: <b>{keyboard}</b>.\n\nХочаце дазволіць яе выкарыстанне?");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(невядома)");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_TITLE, "Запыт дазволу");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Падказка: вы можаце задаць пастаянныя правілы для гэтага ў файле канфігурацыі Hyprland.");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_ALLOW, "Дазволіць");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Дазволіць і запомніць");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_ALLOW_ONCE, "Дазволіць аднойчы");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_DENY, "Забараніць");
huEngine->registerEntry("be_BY", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Невядомая праграма (Ідэнтыфікатар кліента wayland {wayland_id})");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Выглядае, што вашая пераменная асяроддзя XDG_CURRENT_DESKTOP зададзеная звонку, цяперашняе значэнне: {value}.\nГэта можа выклікаць праблемы, калі "
"гэта не зроблена наўмысна.");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_NO_GUIUTILS,
"У вашай сістэме не ўсталяваны hyprland-guiutils, што выкарыстоўваецца для некаторых дыялогавых вокнаў. Разгледзьце ўсталёўку пакета.");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland не змог загрузіць {count} важны рэсурс, вінавацьце ў гэтым адказнага за зборку пакетаў для свайго дыстрыбутыва!";
return "Hyprland не змог загрузіць {count} важных рэсурсаў, вінавацьце ў гэтым адказнага за зборку пакетаў для свайго дыстрыбутыва!";
});
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Макет манітораў наладжаны некарэктна. Манітор {name} накладаецца на іншы(я) манітор(ы).\nДля падрабязнасцей звярніцеся да Wiki (Старонка Monitors). "
"Гэта <b>абавязкова</b> створыць праблемы.");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Манітор {name} не змог наладзіць ніводны з запатрабаваных рэжымаў, аварыйна ўжыты рэжым {mode}.");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Няверна зададзены маштаб для манітора {name}: {scale}, ужываецца прапанаваны маштаб: {fixed_scale}");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Не атрымалася загрузіць плагін {name}: {error}");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Не атрымалася перазагрузіць шэйдар CM, аварыйна ўжываецца rgba/rgbx.");
huEngine->registerEntry("be_BY", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Манітор {name}: пашыраны каляровы дыяпазон уключаны, але экран не ў рэжыме 10-біт.");
// en_US (English)
huEngine->registerEntry("en_US", TXT_KEY_ANR_TITLE, "Application Not Responding");
huEngine->registerEntry("en_US", TXT_KEY_ANR_CONTENT, "An application {title} - {class} is not responding.\nWhat do you want to do with it?");
huEngine->registerEntry("en_US", TXT_KEY_ANR_OPTION_TERMINATE, "Terminate");
huEngine->registerEntry("en_US", TXT_KEY_ANR_OPTION_WAIT, "Wait");
huEngine->registerEntry("en_US", TXT_KEY_ANR_PROP_UNKNOWN, "(unknown)");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "An application <b>{app}</b> is requesting an unknown permission.");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "An application <b>{app}</b> is trying to capture your screen.\n\nDo you want to allow it to?");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "An application <b>{app}</b> is trying to load a plugin: <b>{plugin}</b>.\n\nDo you want to allow it to?");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "A new keyboard has been detected: <b>{keyboard}</b>.\n\nDo you want to allow it to operate?");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(unknown)");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_TITLE, "Permission request");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Hint: you can set persistent rules for these in the Hyprland config file.");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW, "Allow");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Allow and remember");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_ALLOW_ONCE, "Allow once");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_DENY, "Deny");
huEngine->registerEntry("en_US", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Unknown application (wayland client ID {wayland_id})");
huEngine->registerEntry(
"en_US", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Your XDG_CURRENT_DESKTOP environment seems to be managed externally, and the current value is {value}.\nThis might cause issues unless it's intentional.");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_NO_GUIUTILS,
"Your system does not have hyprland-guiutils installed. This is a runtime dependency for some dialogs. Consider installing it.");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland failed to load {count} essential asset, blame your distro's packager for doing a bad job at packaging!";
return "Hyprland failed to load {count} essential assets, blame your distro's packager for doing a bad job at packaging!";
});
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Your monitor layout is set up incorrectly. Monitor {name} overlaps with other monitor(s) in the layout.\nPlease see the wiki (Monitors page) for "
"more. This <b>will</b> cause issues.");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} failed to set any requested modes, falling back to mode {mode}.");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Invalid scale passed to monitor {name}: {scale}, using suggested scale: {fixed_scale}");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Failed to load plugin {name}: {error}");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader reload failed, falling back to rgba/rgbx.");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: wide color gamut is enabled but the display is not in 10-bit mode.");
// as_IN (Assamese)
huEngine->registerEntry("as_IN", TXT_KEY_ANR_TITLE, "এপ্লিকেচনে উত্তৰ দিয়া নাই");
huEngine->registerEntry("as_IN", TXT_KEY_ANR_CONTENT, "এপ্লিকেচন {title} - {class}-এ উত্তৰ দিয়া নাই।\nআপুনি এয়াৰ লগত কি কৰিব বিচাৰে?");
huEngine->registerEntry("as_IN", TXT_KEY_ANR_OPTION_TERMINATE, "সমাপ্ত কৰক");
huEngine->registerEntry("as_IN", TXT_KEY_ANR_OPTION_WAIT, "অপেক্ষা কৰক");
huEngine->registerEntry("as_IN", TXT_KEY_ANR_PROP_UNKNOWN, "(অজ্ঞাত)");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "এপ্লিকেচন <b>{app}</b>-এ এটা অজ্ঞাত অনুমতি বিচাৰিছে।");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "এটা এপ্লিকেচন <b>{app}</b>-এ আপোনাৰ স্ক্ৰীণ কেপচাৰ কৰিবলৈ চেষ্টা কৰিছে।\n\nআপুনি ইয়াক অনুমতি দিব বিচাৰেনে?");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_PLUGIN,
"এপ্লিকেচন <b>{app}</b>-এ এটা প্লাগিন লোড কৰিবলৈ চেষ্টা কৰিছে: <b>{plugin}</b>।\n\nআপুনি ইয়াক অনুমতি দিব বিচাৰেনে?");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "এটা নতুন কিবৰ্ড ধৰা পৰিছে: <b>{keyboard}</b>।\n\nআপুনি ইয়াক চলাবলৈ অনুমতি দিব বিচাৰেনে?");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(অজ্ঞাত)");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_TITLE, "অনুমতিৰ অনুৰোধ");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "ইঙ্গিত: আপুনি হাইপাৰলেণ্ড কনফিগ ফাইলত এইবোৰৰ বাবে স্থায়ী নিয়ম স্থাপন কৰিব পাৰে।");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_ALLOW, "অনুমতি দিয়ক");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "অনুমতি দি মনত ৰাখক");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_ALLOW_ONCE, "এবাৰ অনুমতি দিয়ক");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_DENY, "অস্বীকাৰ কৰক");
huEngine->registerEntry("as_IN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "অজ্ঞাত এপ্লিকেচন (ৱেইলেণ্ড ক্লায়েণ্ট আইডি {wayland_id})");
huEngine->registerEntry(
"as_IN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"আপোনাৰ XDG_CURRENT_DESKTOP পৰিৱেশটো বাহ্যিকভাৱে পৰিচালিত হোৱা যেন লাগিছে, আৰু বৰ্তমানৰ মান হৈছে {value}।\nযদি ই ইচ্ছাকৃতভাৱে নহয়, তেনে হলে সমস্যাৰ সৃষ্টি হ'ব পাৰে।");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_NO_GUIUTILS,
"আপোনাৰ চিষ্টেমত hyprland-guiutils ইনষ্টল কৰা নাই। কিছুমান ডাইলগৰ বাবে ই এটা ৰানটাইম নিৰ্ভৰশীলতা। ইয়াক ইনষ্টল কৰাৰ কথা চিন্তা কৰক।");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_FAILED_ASSETS,
"হাইপাৰলেণ্ড {count}-টা প্ৰয়োজনীয় সম্পদ লোড কৰাত অসফল হৈছে, বেয়া পেকজিং কৰাৰ বাবে আপোনাৰ ডিষ্ট্ৰ'ৰ পেকেজাৰক দোষাৰোপ কৰক!");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"আপোনাৰ মনিটৰৰ লেআউট ভুলকৈ ছেট কৰা হৈছে। মনিটৰ {name} লেআউটত আন মনিটৰ(সমূহ)ৰ সৈতে ওপৰা-উপৰি হৈ আছে।\nঅধিক তথ্যৰ বাবে অনুগ্ৰহ কৰি ৱিকি (মনিটৰ পৃষ্ঠা) চাওক। ই "
"<b>সমস্যাৰ</b> সৃষ্টি কৰিব।");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "মনিটৰ {name}-এ কোনো অনুৰোধ কৰা মোড ছেট কৰাত অসফল হৈছে, মোড {mode}-লৈ ঘূৰি আহিছে।");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "মনিটৰ {name}: {scale}-লৈ অবৈধ মাপন দিয়া হৈছে, পৰামৰ্শ দিয়া মাপন ব্যৱহাৰ কৰা যাব: {fixed_scale}");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "প্লাগিন {name} লোড কৰাত অসফল হৈছে: {error}");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM শ্বেডাৰ ৰিলোড কৰাত অসফল হৈছে, rgba/rgbx-লৈ ঘূৰি আহিছে।");
huEngine->registerEntry("as_IN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "প্ৰসাৰিত ৰঙৰ বৰ্গ সক্ষম কৰা হৈছে কিন্তু ডিচপ্লে 10-বিট মোডত নাই।");
// de_DE (German)
huEngine->registerEntry("de_DE", TXT_KEY_ANR_TITLE, "Anwendung Reagiert Nicht");
huEngine->registerEntry("de_DE", TXT_KEY_ANR_CONTENT, "Eine Anwendung {title} - {class} reagiert nicht.\nWas möchten Sie damit tun?");
huEngine->registerEntry("de_DE", TXT_KEY_ANR_OPTION_TERMINATE, "Beenden");
huEngine->registerEntry("de_DE", TXT_KEY_ANR_OPTION_WAIT, "Warten");
huEngine->registerEntry("de_DE", TXT_KEY_ANR_PROP_UNKNOWN, "(unbekannt)");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Eine Anwendung <b>{app}</b> fordert eine unbekannte Berechtigung an.");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Eine Anwendung <b>{app}</b> versucht Ihren Bildschrim aufzunehmen.\n\nMöchten Sie dies erlauben?");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Eine Anwendung <b>{app}</b> versucht ein Plugin zu laden: <b>{plugin}</b>.\n\nMöchten Sie dies erlauben?");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Eine neue Tastatur wurde erkannt: <b>{keyboard}</b>.\n\nMöchten Sie diese in Betrieb nehmen?");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(unbekannt)");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_TITLE, "Berechtigungsanfrage");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: Sie können dafür permanente Regeln in der Hyprland-Konfigurationsdatei festlegen.");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_ALLOW, "Erlauben");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Erlauben und merken");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_ALLOW_ONCE, "Einmal erlauben");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_DENY, "Verweigern");
huEngine->registerEntry("de_DE", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Unbekannte Anwendung (wayland client ID {wayland_id})");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Ihre XDG_CURRENT_DESKTOP umgebung scheint extern gemanagt zu werden, und der aktuelle Wert ist {value}.\nDies könnte zu Problemen führen sofern es "
"nicht absichtlich so ist.");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_NO_GUIUTILS,
"Ihr System hat hyprland-guiutils nicht installiert. Dies ist eine Laufzeitabhängigkeit für einige Dialoge. Es ist empfohlen diese zu installieren.");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland konnte {count} essentielle Ressource nicht laden, geben Sie dem Packager ihrer Distribution die Schuld für ein schlechtes Package!";
return "Hyprland konnte {count} essentielle Ressroucen nicht laden, geben Sie dem Packager ihrer Distribution die Schuld für ein schlechtes Package!";
});
huEngine->registerEntry(
"de_DE", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Ihr Bildschirmlayout ist fehlerhaft aufgesetzt. Der Bildschirm {name} überlappt mit anderen Bildschirm(en) im Layout.\nBitte siehe im Wiki (Monitors Seite) für "
"mehr Informationen. Dies <b>wird</b> zu Problemen führen.");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Bildschirm {name} konnte keinen der angeforderten Modi setzen fällt auf den Modus {mode} zurück.");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
"Ungültiger Skalierungsfaktor {scale} für Bildschirm {name}, es wird der empfohlene Faktor {fixed_scale} verwendet.");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Plugin {name} konnte nicht geladen werden: {error}");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader konnte nicht neu geladen werden und es wird auf rgba/rgbx zurückgefallen.");
huEngine->registerEntry("de_DE", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Bildschirm {name}: wide color gamut ist aktiviert aber der Bildschirm ist nicht im 10-bit Modus.");
// de_CH (Swiss German)
huEngine->registerEntry("de_CH", TXT_KEY_ANR_TITLE, "Aawändig Reagiert Ned");
huEngine->registerEntry("de_CH", TXT_KEY_ANR_CONTENT, "En Aawändig {title} - {class} reagiert ned.\nWas wend Sie demet mache?");
huEngine->registerEntry("de_CH", TXT_KEY_ANR_OPTION_TERMINATE, "Beände");
huEngine->registerEntry("de_CH", TXT_KEY_ANR_OPTION_WAIT, "Warte");
huEngine->registerEntry("de_CH", TXT_KEY_ANR_PROP_UNKNOWN, "(onbekannt)");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "En Aawändig <b>{app}</b> fordert en onbekannti Berächtigong aa.");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "En Aawändig <b>{app}</b> versuecht Ehre Beldscherm uufznäh.\n\nWend Sie das erlaube?");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "En Aawändig <b>{app}</b> versuecht es Plugin z'lade: <b>{plugin}</b>.\n\nWend Sie das erlaube?");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "En neui Tastatur esch erkönne worde: <b>{keyboard}</b>.\n\nWend sie die in Betreb nä?");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(onbekannt)");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_TITLE, "Berächtigongsaafrog");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: Sie chönd permanenti Regle deför i ehrere Hyprland-Konfigurationsdatei festlegge.");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_ALLOW, "Erlaube");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Erlaube ond merke");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_ALLOW_ONCE, "Einisch erlaube");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_DENY, "Verweigere");
huEngine->registerEntry("de_CH", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Onbekannti Aawändig (wayland client ID {wayland_id})");
huEngine->registerEntry(
"de_CH", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Ehri XDG_CURRENT_DESKTOP omgäbig schiint extern gmanagt z'wärde, ond de aktuelli Wärt esch {value}.\nDas chönnt zo Problem füehre sofärn das ned absechtlech so esch.");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_NO_GUIUTILS,
"Ehres System hed hyprland-guiutils ned installiert. Das esch en Laufziitabhängigkeit för es paar Dialog. Es werd empfohle sie z'installiere.");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_FAILED_ASSETS,
"Hyprland hed {count} essentielli Ressource ned chönne lade, gäbed Sie im Packager vo ehrere Distribution schold för es schlächts Package!");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Ehres Beldschermlayout esch fählerhaft uufgsetzt. De Beldscherm {name} öberlappt met andere Beldscherm(e) im Layout.\nBitte lueged sie im Wiki "
"(Monitors Siite) för meh Informatione. Das <b>werd</b> zo Problem füehre.");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "De Beldscherm {name} hed keine vode aagforderete Modi chönne setze, ond fallt uf de Modus {mode} zrogg.");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
"Ongöltige Skalierigsfaktor {scale} för de Beldscherm {name}, es werd de empfohleni Faktor {fixed_scale} verwändet.");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "S Plugin {name} hed ned chönne glade wärde: {error}");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader hed ned chönne neu glade wärde, es werd uf rgba/rgbx zrogggfalle.");
huEngine->registerEntry("de_CH", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Beldscherm {name}: wide color gamut esch aktiviert aber de Beldscherm esch ned im 10-bit Modus.");
// pt_BR (Brazilian Portuguese)
huEngine->registerEntry("pt_BR", TXT_KEY_ANR_TITLE, "O aplicativo não está respondendo");
huEngine->registerEntry("pt_BR", TXT_KEY_ANR_CONTENT, "O aplicativo {title} - {class} não está respondendo.\nO que você deseja fazer?");
huEngine->registerEntry("pt_BR", TXT_KEY_ANR_OPTION_TERMINATE, "Encerrar");
huEngine->registerEntry("pt_BR", TXT_KEY_ANR_OPTION_WAIT, "Esperar");
huEngine->registerEntry("pt_BR", TXT_KEY_ANR_PROP_UNKNOWN, "(Desconhecido)");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "O aplicativo <b>{app}</b> está pedindo uma permissão desconhecida.");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "O aplicativo <b>{app}</b> está tentando capturar sua tela.\n\nVocê deseja permitir?");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "O aplicativo <b>{app}</b> está tentando carregar um plugin: <b>{plugin}</b>.\n\nVocê deseja permitir?");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Um novo teclado foi detectado: <b>{keyboard}</b>.\n\nVocê deseja permitir seu uso?");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(Desconhecido)");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_TITLE, "Solicitação de permissão");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_PERSISTENCE_HINT,
"Dica: você pode definir regras persistentes para essas permissões no arquivo de configuração do Hyprland");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_ALLOW, "Permitir");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permitir e lembrar");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permitir uma vez");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_DENY, "Negar");
huEngine->registerEntry("pt_BR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Aplicativo desconhecido (wayland client ID {wayland_id})");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Seu XDG_CURRENT_DESKTOP parece estar sendo gerenciado externamente, e atualmente é {value}.\nIsso pode causar problemas caso não seja intencional.");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_NO_GUIUTILS,
"Seu sistema não possui hyprland-guiutils instalado. Essa é uma dependência de execução para alguns diálogos. Considere instalá-lo.");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "O Hyprland falhou ao carregar {count} recurso essencial, culpe o empacotador da sua distro por fazer um péssimo trabalho!";
return "O Hyprland falhou ao carregar {count} recursos essenciais, culpe o empacotador da sua distro por fazer um péssimo trabalho!";
});
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Sua disposição de monitores está configurada incorretamente. O monitor {name} se sobrepõe a outro(s) monitor(es) na disposição.\nPor favor consulte "
"a wiki (Monitors page) para "
"mais informações. Isso <b>vai</b> causar problemas.");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "O monitor {name} falhou em definir qualquer um dos modos solicitados, voltando ao modo {mode}.");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
"Um fator de escala inválido foi passado para o monitor {name}: {scale}, usando o fator sugerido: {fixed_scale}");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Falha ao carregar o plugin {name}: {error}");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Falha ao carregar o shader CM, voltando para rgba/rgbx.");
huEngine->registerEntry("pt_BR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: o modo de gama de cores amplo está ativado, mas a tela não está configurada para 10 bits.");
// es (Spanish)
huEngine->registerEntry("es", TXT_KEY_ANR_TITLE, "La aplicación no responde");
huEngine->registerEntry("es", TXT_KEY_ANR_CONTENT, "Una aplicación {title} - {class} no responde.\n¿Qué quieres hacer?");
huEngine->registerEntry("es", TXT_KEY_ANR_OPTION_TERMINATE, "Terminar");
huEngine->registerEntry("es", TXT_KEY_ANR_OPTION_WAIT, "Esperar");
huEngine->registerEntry("es", TXT_KEY_ANR_PROP_UNKNOWN, "(desconocido)");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Una aplicación <b>{app}</b> está solicitando un permiso desconocido.");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Una aplicación <b>{app}</b> está intentando capturar la pantalla.\n\n¿Quieres permitirlo?");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Una aplicación <b>{app}</b> está intentando cargar un plugin: <b>{plugin}</b>.\n\n¿Quieres permitirlo?");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Se ha detectado un nuevo teclado: <b>{keyboard}</b>.\n\n¿Quieres permitir su funcionamiento?");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(desconocido)");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_TITLE, "Solicitud de permiso");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Sugerencia: puedes establecer reglas persistentes para estos en el archivo de configuración de Hyprland.");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_ALLOW, "Permitir");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permitir y recordar");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permitir una vez");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_DENY, "Denegar");
huEngine->registerEntry("es", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Aplicación desconocida (wayland client ID {wayland_id})");
huEngine->registerEntry("es", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"La variable de entorno XDG_CURRENT_DESKTOP parece estar gestionada externamente, y el valor actual es {value}.\nEsto podría causar problemas, a menos "
"que sea intencionado.");
huEngine->registerEntry(
"es", TXT_KEY_NOTIF_NO_GUIUTILS,
"Tu sistema no tiene instalado hyprland-guiutils. Se trata de una dependencia de tiempo de ejecución para algunos diálogos. Considera la posibilidad de instalarlo.");
huEngine->registerEntry("es", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "No se ha podido cargar {count} recurso clave, ¡culpa a tu empaquetador por hacer un mal trabajo!";
return "No se ha podido cargar {count} recursos clave, ¡culpa a tu empaquetador por hacer un mal trabajo!";
});
huEngine->registerEntry(
"es", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"La configuración de su monitor no es correcta. El monitor {name} se superpone con otros monitores en la configuración. Consulte la wiki (página Monitors, en inglés) "
"para obtener más información. Esto <b>provocará</b> problemas.");
huEngine->registerEntry("es", TXT_KEY_NOTIF_MONITOR_MODE_FAIL,
"El monitor {name} no ha podido configurar ninguno de los modos solicitados, por lo que ha recurrido al modo {mode}.");
huEngine->registerEntry("es", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Escala no válida pasada al monitor {name}: {scale}, utilizando la escala sugerida: {fixed_scale}");
huEngine->registerEntry("es", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Error al cargar el plugin {name}: {error}");
huEngine->registerEntry("es", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Error al recargar el sombreador CM, recurriendo a rgba/rgbx.");
huEngine->registerEntry("es", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: la gama de colores amplia está habilitada, pero la pantalla no está en modo de 10-bit.");
// fa_IR (Persian)
huEngine->registerEntry("fa_IR", TXT_KEY_ANR_TITLE, "برنامه پاسخ نمی‌دهد");
huEngine->registerEntry("fa_IR", TXT_KEY_ANR_CONTENT, "برنامه {title} - {class} پاسخی نمی‌دهد.\nمی‌خواهید چه کاری انجام شود؟");
huEngine->registerEntry("fa_IR", TXT_KEY_ANR_OPTION_TERMINATE, "بستن برنامه");
huEngine->registerEntry("fa_IR", TXT_KEY_ANR_OPTION_WAIT, "صبر کنید");
huEngine->registerEntry("fa_IR", TXT_KEY_ANR_PROP_UNKNOWN, "(نامشخص)");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "برنامه <b>{app}</b> در حال درخواست یک مجوز ناشناخته است.");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY,
"برنامه <b>{app}</b> می‌خواهد صفحه‌نمایش شما را ضبط کند.\n\nآیا اجازه می‌دهید؟");
huEngine->registerEntry(
"fa_IR", TXT_KEY_PERMISSION_REQUEST_PLUGIN,
"برنامه <b>{app}</b> می‌خواهد پلاگین <b>{plugin}</b> را بارگذاری کند.\n\nآیا اجازه می‌دهید پلاگین بارگذاری "
"شود؟");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD,
"یک کیبورد جدید شناسایی شد: <b>{keyboard}</b>.\n\nآیا اجازه استفاده از آن را صادر می‌کنید؟");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(نامشخص)");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_TITLE, "درخواست مجوز");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_PERSISTENCE_HINT,
"نکته: می‌توانید قوانین دائمی مرتبط را در فایل تنظیمات هایپرلند تعریف کنید.");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_ALLOW, "اجازه");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "اجازه و ذخیره");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_ALLOW_ONCE, "اجازه یک‌بار");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_DENY, "عدم اجازه");
huEngine->registerEntry("fa_IR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "برنامه ناشناخته (شناسه Wayland: {wayland_id})");
huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"متغیر XDG_CURRENT_DESKTOP توسط محیطی خارجی تنظیم شده است و مقدار فعلی آن {value} است.\n"
"اگر این کار عمدی نباشد ممکن است باعث ایجاد مشکل شود.");
huEngine->registerEntry(
"fa_IR", TXT_KEY_NOTIF_NO_GUIUTILS,
"بستهٔ hyprland-guiutils در سیستم نصب نیست. این بسته برای برخی از پنجره‌ها و دیالوگ‌ها لازم است. نصب "
"آن "
"پیشنهاد "
"می‌شود.");
huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "هایپرلند نتوانست یک فایل ضروری را بارگذاری کند؛ ممکن است بسته‌بندی توزیع مشکل داشته "
"باشد.";
return "هایپرلند نتوانست {count} فایل ضروری را بارگذاری کند؛ ممکن است بسته‌بندی توزیع مشکل داشته "
"باشد.";
});
huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"چیدمان مانیتورها صحیح نیست. مانیتور {name} با یک یا چند مانیتور دیگر تداخل دارد.\n"
"برای اطلاعات بیشتر به صفحهٔ مانیتورها در ویکی مراجعه کنید. این موضوع <b>حتماً</b> باعث مشکل "
"می‌شود.");
huEngine->registerEntry(
"fa_IR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL,
"مانیتور {name} نتوانست هیچ‌کدام از حالت‌های درخواستی را اعمال کند؛ بازگشت به حالت {mode}.");
huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "مقیاس واردشده برای مانیتور {name} نامعتبر است: {scale}. مقیاس پیشنهادی اعمال شد: {fixed_scale}");
huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "بارگذاری پلاگین {name} با خطا روبه‌رو شد: {error}");
huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "بارگذاری دوبارهٔ شیدر CM ناموفق بود؛ از حالت rgba/rgbx استفاده شد.");
huEngine->registerEntry("fa_IR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "مانیتور {name}: گسترهٔ رنگ وسیع فعال است اما نمایشگر در حالت ۱۰ بیتی نیست.");
// fr_FR (French)
huEngine->registerEntry("fr_FR", TXT_KEY_ANR_TITLE, "L'application ne répond plus");
huEngine->registerEntry("fr_FR", TXT_KEY_ANR_CONTENT, "L'application {title} - {class} ne répond plus.\nQue voulez-vous faire?");
huEngine->registerEntry("fr_FR", TXT_KEY_ANR_OPTION_TERMINATE, "Forcer l'arrêt");
huEngine->registerEntry("fr_FR", TXT_KEY_ANR_OPTION_WAIT, "Attendre");
huEngine->registerEntry("fr_FR", TXT_KEY_ANR_PROP_UNKNOWN, "(inconnu)");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Une application <b>{app}</b> demande une autorisation inconnue.");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Une application <b>{app}</b> tente de capturer votre écran.\n\nVoulez-vous l'y autoriser?");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Une application <b>{app}</b> tente de charger un module : <b>{plugin}</b>.\n\nVoulez-vous l'y autoriser?");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Un nouveau clavier a été détecté : <b>{keyboard}</b>.\n\nVouslez-vous l'autoriser à fonctioner?");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(inconnu)");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_TITLE, "Demande d'autorisation");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Astuce: vous pouvez définir des règles persistantes dans le fichier de configuration de Hyprland.");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_ALLOW, "Autoriser");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Autoriser et mémoriser");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_ALLOW_ONCE, "Autoriser une fois");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_DENY, "Refuser");
huEngine->registerEntry("fr_FR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Application inconnue (ID client wayland {wayland_id})");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Votre variable d'environnement XDG_CURRENT_DESKTOP semble être gérée de manière externe, et sa valeur actuelle est {value}.\nCela peut provoquer des "
"problèmes si ce n'est pas intentionnel.");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_NO_GUIUTILS,
"Vous système n'a pas hyprland-guiutils installé. C'est une dépendance d'éxécution pour certains dialogues. Envisagez de l'installer.");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland n'a pas pu charger {count} ressource essentielle, cela indique très probablement un problème dans le paquet de votre distribution.";
return "Hyprland n'a pas pu charger {count} ressources essentielles, cela indique très probablement un problème dans le paquet de votre distribution.";
});
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Votre disposition d'écrans est incorrecte. Le moniteur {name} chevauche un ou plusieurs autres.\nVeuillez consulter le wiki (page Moniteurs) pour"
"en savoir plus. Cela <b>causera</> des problèmes.");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Le moniteur {name} n'a pu appliquer aucun des modes demandés, retour au mode {mode}.");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Échelle invalide pour le moniteur {name}: {scale}. Utilisation de l'échelle suggérée: {fixed_scale}.");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Échec du chargement du module {name} : {error}");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Le rechargement du shader CM a échoué, retour aux formats rgba/rgbx");
huEngine->registerEntry("fr_FR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Moniteur {name} : l'espace colorimétrique étendu est activé, mais l'écran n'est pas en mode 10-bits.");
// hi_IN (Hindi)
huEngine->registerEntry("hi_IN", TXT_KEY_ANR_TITLE, "एप्लिकेशन प्रतिक्रिया नहीं दे रहा है");
huEngine->registerEntry("hi_IN", TXT_KEY_ANR_CONTENT,
"एक एप्लिकेशन {title} - {class} प्रतिक्रिया नहीं दे रहा "
"है।\nआप इसके साथ क्या करना चाहेंगे?");
huEngine->registerEntry("hi_IN", TXT_KEY_ANR_OPTION_TERMINATE, "समाप्त करें");
huEngine->registerEntry("hi_IN", TXT_KEY_ANR_OPTION_WAIT, "इंतजार करें");
huEngine->registerEntry("hi_IN", TXT_KEY_ANR_PROP_UNKNOWN, "(अज्ञात)");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "एक एप्लिकेशन <b>{app}</b> एक अज्ञात अनुमति का अनुरोध कर रहा है।");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY,
"एक एप्लिकेशन <b>{app}</b> आपकी स्क्रीन कैप्चर करने की "
"कोशिश कर रहा है।\n\nक्या आप इसे अनुमति देना चाहते हैं?");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_PLUGIN,
"एक एप्लिकेशन <b>{app}</b> एक प्लगइन लोड करने की कोशिश कर रहा है: "
"<b>{plugin}</b>.\n\nक्या आप इसे अनुमति देना चाहते हैं?");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD,
"नया कीबोर्ड पाया गया: <b>{keyboard}</b>.\n\nक्या आप "
"इसे काम करने की अनुमति देना चाहते हैं?");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(अज्ञात)");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_TITLE, "अनुमति अनुरोध");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "संकेत: आप Hyprland कॉन्फ़िग फ़ाइल में इनके लिए स्थायी नियम सेट कर सकते हैं।");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_ALLOW, "अनुमति दें");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "अनुमति दें और याद रखें");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_ALLOW_ONCE, "एक बार अनुमति दें");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_DENY, "अस्वीकार करें");
huEngine->registerEntry("hi_IN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "अज्ञात एप्लिकेशन (wayland क्लाइंट ID {wayland_id})");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"आपका XDG_CURRENT_DESKTOP परिवेश बाहरी रूप से प्रबंधित लगता है, और वर्तमान मान "
"{value} है।\nयह समस्या पैदा कर सकता "
"है जब तक कि यह जानबूझकर न किया गया हो।");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_NO_GUIUTILS,
"आपके सिस्टम में hyprland-guiutils इंस्टॉल नहीं है। यह कुछ संवादों के लिए एक रनटाइम "
"निर्भरता है। इसे इंस्टॉल करने पर विचार करें।");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland {count} आवश्यक संसाधन लोड करने में विफल रहा, अपने डिस्ट्रो "
"के पैकेजर को पैकेजिंग में खराब काम करने का दोष दें!";
return "Hyprland {count} आवश्यक संसाधनों को लोड करने में विफल रहा, अपने "
"डिस्ट्रो के पैकेजर को पैकेजिंग में खराब काम करने का दोष दें!";
});
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"आपका मॉनिटर लेआउट गलत तरीके से सेट है। मॉनिटर {name} लेआउट में अन्य मॉनिटर(ओं) के "
"साथ ओवरलैप कर रहा है।\nकृपया विकि "
" (Monitors पेज) देखें। यह <b>समस्याएँ</b> पैदा करेगा।");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL,
"मॉनिटर {name} ने किसी भी अनुरोधित मोड को सेट करने में "
"विफल रहा, मोड {mode} पर वापस जा रहा है।");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
"मॉनिटर {name} को अवैध स्केल दिया गया: {scale}, सुझाया "
"गया स्केल इस्तेमाल किया जा रहा है: {fixed_scale}");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "प्लगइन {name} लोड करने में विफल: {error}");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM शेडर रीलोड विफल हुआ, rgba/rgbx पर वापस जा रहा है।");
huEngine->registerEntry("hi_IN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "मॉनिटर {name}: वाइड कलर गैम सक्षम है लेकिन डिस्प्ले 10-बिट मोड में नहीं है।");
// it_IT (Italian)
huEngine->registerEntry("it_IT", TXT_KEY_ANR_TITLE, "L'applicazione non risponde");
huEngine->registerEntry("it_IT", TXT_KEY_ANR_CONTENT, "Un'applicazione {title} - {class} non risponde.\nCosa vuoi fare?");
huEngine->registerEntry("it_IT", TXT_KEY_ANR_OPTION_TERMINATE, "Termina");
huEngine->registerEntry("it_IT", TXT_KEY_ANR_OPTION_WAIT, "Attendi");
huEngine->registerEntry("it_IT", TXT_KEY_ANR_PROP_UNKNOWN, "(sconosciuto)");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Un'applicazione <b>{app}</b> richiede un'autorizzazione sconosciuta.");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Un'applicazione <b>{app}</b> sta provando a catturare il tuo schermo.\n\nGlie lo vuoi permettere?");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_PLUGIN,
"Un'applicazione <b>{app}</b> sta provando a caricare un plugin: <b>{plugin}</b>.\n\nGlie lo vuoi permettere?");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "È stata rilevata una nuova tastiera: <b>{keyboard}</b>.\n\nLe vuoi permettere di operare?");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(sconosciuto)");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_TITLE, "Richiesta di autorizzazione");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Consiglio: Puoi impostare una regola persistente nel tuo file di configurazione di Hyprland.");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW, "Permetti");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Permetti e ricorda");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_ALLOW_ONCE, "Permetti una volta");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_DENY, "Nega");
huEngine->registerEntry("it_IT", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Applicazione sconosciuta (wayland client ID {wayland_id})");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"L'ambiente XDG_CURRENT_DESKTOP sembra essere gestito esternamente, il valore attuale è {value}.\nSe non è voluto, potrebbe causare problemi.");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_NO_GUIUTILS,
"Sembra che hyprland-guiutils non sia installato. È una dipendenza richiesta per alcuni dialoghi che potresti voler installare.");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_FAILED_ASSETS,
"Hyprland non ha potuto caricare {count} asset, dai la colpa al packager della tua distribuzione per il suo cattivo lavoro!");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"I tuoi schermi sono configurati incorrettamente. Lo schermo {name} si sovrappone con altri nel layout.\nConsulta la wiki (voce Schermi) per "
"altre informazioni. Questo <b>causerà</b> problemi.");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Lo schermo {name} non ha potuto impostare alcuna modalità richiesta, sarà usata la modalità {mode}.");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
"Fattore di scala non valido per lo schermo {name}: {scale}, utilizzando il fattore suggerito: {fixed_scale}");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Impossibile caricare il plugin {name}: {error}");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Impossibile ricaricare gli shader CM, sarà usato rgba/rgbx.");
huEngine->registerEntry("it_IT", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Schermo {name}: la gamma di colori ampia è abilitata ma lo schermo non è in modalità 10-bit.");
// ja_JP (Japanese)
huEngine->registerEntry("ja_JP", TXT_KEY_ANR_TITLE, "アプリは応答しません");
huEngine->registerEntry("ja_JP", TXT_KEY_ANR_CONTENT, "アプリ {title} ー {class}は応答しません。\n何をしたいですか?");
huEngine->registerEntry("ja_JP", TXT_KEY_ANR_OPTION_TERMINATE, "強制終了");
huEngine->registerEntry("ja_JP", TXT_KEY_ANR_OPTION_WAIT, "待機");
huEngine->registerEntry("ja_JP", TXT_KEY_ANR_PROP_UNKNOWN, "(不明)");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "アプリ<b>{app}</b>は不明な許可を要求します。");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "アプリ<b>{app}</b>は画面へのアクセスを要求します。\n\n許可したいですか?");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "アプリ<b>{app}</b>は以下のプラグインをロード許可を要求します:<b>{plugin}</b>。\n\n許可したいですか?");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "新しいキーボードを見つけた:<b>{keyboard}</b>。\n\n稼働を許可したいですか?");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(不明)");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_TITLE, "許可要求");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "ヒントHyprlandのコンフィグで通常の許可や却下を設定できます。");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW, "許可");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "保存して許可");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_ALLOW_ONCE, "一度許可");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_DENY, "却下");
huEngine->registerEntry("ja_JP", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "不明なアプリ (waylandクライアントID {wayland_id})");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"エンバイアロンメント変数「XDG_CURRENT_DESKTOP」は外部から「{value}」に設定しました。\n意図的ではなければ、問題は発生可能性があります。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_NO_GUIUTILS, "システムにhyprland-guiutilsはインストールしていません。このパッケージをインストールしてください。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_FAILED_ASSETS,
"{count}つの根本的なアセットをロードできませんでした。これはパッケージャーのせいだから、パッケージャーに文句してください。");
huEngine->registerEntry(
"ja_JP", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"画面の位置設定は誤用です。画面{name}は他の画面の区域と重ね合わせます。\nウィキのモニターページで詳細を確認してください。これは<b>絶対に</b>問題になります。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "画面{name}は設定したモードを正常に受け入れませんでした。{mode}を使いました。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "画面{name}のスケールは無効:{scale}、代わりにおすすめのスケール{fixed_scale}を使いました。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "プラグイン{name}のロード失敗: {error}");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CMシェーダーのリロード失敗、rgba/rgbxを使いました。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "画面{name}広い色域は設定していますけど、画面は10ビットモードに設定されていません。");
// hu_HU (Hungarian)
huEngine->registerEntry("hu_HU", TXT_KEY_ANR_TITLE, "Az alkalmazás nem válaszol");
huEngine->registerEntry("hu_HU", TXT_KEY_ANR_CONTENT, "A(z) {title} - {class} alkalmazás nem válaszol.\nMit szeretne tenni vele?");
huEngine->registerEntry("hu_HU", TXT_KEY_ANR_OPTION_TERMINATE, "Leállítás");
huEngine->registerEntry("hu_HU", TXT_KEY_ANR_OPTION_WAIT, "Várakozás");
huEngine->registerEntry("hu_HU", TXT_KEY_ANR_PROP_UNKNOWN, "(ismeretlen)");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "A(z) <b>{app}</b> alkalmazás ismeretlen engedélyt kér.");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "A(z) <b>{app}</b> alkalmazás megpróbálja rögzíteni a képernyőjét.\n\nEngedélyezi?");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "A(z) <b>{app}</b> alkalmazás megpróbál egy bővítményt betölteni: <b>{plugin}</b>.\n\nEngedélyezi?");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Új billentyűzetet észleltünk: <b>{keyboard}</b>.\n\nEngedélyezi a használatát?");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(ismeretlen)");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_TITLE, "Engedélykérés");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tipp: Állandó szabályokat állíthat be a Hyprland konfigurációs fájlban.");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_ALLOW, "Engedélyezés");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Mindig engedélyez");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_ALLOW_ONCE, "Egyszeri engedélyezés");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_DENY, "Elutasítás");
huEngine->registerEntry("hu_HU", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Ismeretlen alkalmazás (wayland kliens ID {wayland_id})");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Úgy tűnik, hogy az XDG_CURRENT_DESKTOP környezetet külsőleg kezelik, és a jelenlegi érték {value}.\nEz problémákat okozhat, hacsak nem szándékos.");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_NO_GUIUTILS,
"A rendszerében nincs telepítve a hyprland-guiutils. Ez egy futásidejű függőség néhány párbeszédablakhoz. Fontolja meg a telepítését.");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "A Hyprland nem tudta betölteni az 1 szükséges erőforrást. Kérjük, jelezze a hibát a disztribúció csomagolójának.";
return "A Hyprland nem tudott betölteni {count} szükséges erőforrást. Kérjük, jelezze a hibát a disztribúció csomagolójának.";
});
huEngine->registerEntry(
"hu_HU", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"A monitor elrendezése helytelenül van beállítva. A(z) {name} monitor átfedi a többi monitort az elrendezésben.\nKérjük, további információkért tekintse meg a wikit "
"(Monitors oldal). Ez <b>problémákat</b> fog okozni.");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "A(z) {name} monitor nem tudta beállítani a kért módokat, visszaáll a(z) {mode} módra.");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Érvénytelen skálázás a(z) {name} monitorhoz: {scale}, a javasolt skálázás használata: {fixed_scale}");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nem sikerült betölteni a(z) {name} bővítményt: {error}");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "A CM shader újratöltése sikertelen, visszaáll rgba/rgbx-re.");
huEngine->registerEntry("hu_HU", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: A széles színtartomány engedélyezve van, de a kijelző nem 10 bites módban van.");
// ml_IN (Malayalam)
huEngine->registerEntry("ml_IN", TXT_KEY_ANR_TITLE, "ആപ്ലിക്കേഷൻ പ്രതികരിക്കുന്നില്ല");
huEngine->registerEntry("ml_IN", TXT_KEY_ANR_CONTENT, "ആപ്ലിക്കേഷൻ {title} - {class} പ്രതികരിക്കുന്നില്ല.\nഇതിന് നിങ്ങൾ എന്ത് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു?");
huEngine->registerEntry("ml_IN", TXT_KEY_ANR_OPTION_TERMINATE, "അവസാനിപ്പിക്കുക");
huEngine->registerEntry("ml_IN", TXT_KEY_ANR_OPTION_WAIT, "കാത്തിരിക്കുക");
huEngine->registerEntry("ml_IN", TXT_KEY_ANR_PROP_UNKNOWN, "(അജ്ഞാതം)");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "ആപ്ലിക്കേഷൻ <b>{app}</b> ഒരു അജ്ഞാത അനുമതി അഭ്യർത്ഥിക്കുന്നു.");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "ആപ്ലിക്കേഷൻ <b>{app}</b> നിങ്ങളുടെ സ്ക്രീൻ പകർത്താൻ ശ്രമിക്കുന്നു.\n\nനിങ്ങൾ അത് അനുവദിക്കണോ?");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "ആപ്ലിക്കേഷൻ <b>{app}</b> ഒരു പ്ലഗിൻ ലോഡ് ചെയ്യാൻ ശ്രമിക്കുന്നു: <b>{plugin}</b>.\n\nഇത് അനുവദിക്കണോ?");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "പുതിയ കീബോർഡ് കണ്ടെത്തി: <b>{keyboard}</b>.\n\nഇത് പ്രവർത്തിക്കാൻ അനുവദിക്കണോ?");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(അജ്ഞാതം)");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_TITLE, "അനുമതി അഭ്യർത്ഥന");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "സൂചന: Hyprland കോൺഫിഗ് ഫയലിൽ സ്ഥിരനിയമങ്ങൾ സജ്ജമാക്കാം.");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_ALLOW, "അനുവദിക്കുക");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "അനുവദിച്ച് ഓർക്കുക");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_ALLOW_ONCE, "ഒന്നുതവണ അനുവദിക്കുക");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_DENY, "നിരസിക്കുക");
huEngine->registerEntry("ml_IN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "അജ്ഞാത അപ്ലിക്കേഷൻ (wayland client ID {wayland_id})");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"നിങ്ങളുടെ XDG_CURRENT_DESKTOP പരിസ്ഥിതി പുറത്ത് നിന്ന് നിയന്ത്രിക്കപ്പെടുന്നു, ഇപ്പോഴത്തെ മൂല്യം "
"{value}.\nഇത് ഉദ്ദേശ്യമായല്ലെങ്കിൽ പ്രശ്നങ്ങൾ ഉണ്ടാകും.");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_NO_GUIUTILS,
"നിങ്ങളുടെ സിസ്റ്റത്തിൽ hyprland-guiutils ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല. ഇത് ചില ഡയലോഗുകൾക്ക് ആവശ്യമായ "
"റൺടൈം ആശ്രയമാണ്. ഇൻസ്റ്റാൾ ചെയ്യുക.");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland {count} പ്രധാന അസറ്റ് ലോഡുചെയ്യാൻ പരാജയപ്പെട്ടു, നിങ്ങളുടെ "
"ഡിസ്‌ട്രോ "
"പാക്കേജർ പിശക് ചെയ്തിരിക്കുന്നു!";
return "Hyprland {count} പ്രധാന അസറ്റുകൾ ലോഡുചെയ്യാൻ പരാജയപ്പെട്ടു, നിങ്ങളുടെ "
"ഡിസ്‌ട്രോ "
"പാക്കേജർ പിശക് ചെയ്തിരിക്കുന്നു!";
});
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"മോണിറ്റർ ലേയൗട്ട് തെറ്റാണ്. മോണിറ്റർ {name} മറ്റുള്ളവയുമായ് ഒതുങ്ങുന്നു.\nകൂടുതൽ വിവരങ്ങൾക്ക് Wiki "
"(Monitors page) കാണുക. ഇത് <b>പ്രശ്നങ്ങൾ ഉണ്ടാക്കും</b>.");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "മോണിറ്റർ {name} ആവശ്യപ്പെട്ട മോഡുകൾ സജ്ജമാക്കാൻ പരാജയപ്പെട്ടു, ഇപ്പോൾ {mode} ഉപയോഗിക്കുന്നു.");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "മോണിറ്റർ {name} ന് അസാധുവായ സ്കെയിൽ: {scale}, നിർദ്ദേശിച്ച സ്കെയിൽ: {fixed_scale}");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "പ്ലഗിൻ {name} ലോഡ് ചെയ്യാൻ പരാജയപ്പെട്ടു: {error}");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM ഷേഡർ റീലോഡ് പരാജയപ്പെട്ടു, rgba/rgbx ലേക്ക് മാറുന്നു.");
huEngine->registerEntry("ml_IN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "മോണിറ്റർ {name}: വൈഡ് കളർ ഗാമട്ട് പ്രവർത്തനക്ഷമമാണെങ്കിലും, മോഡ് 10-bit അല്ല.");
// nb_NO (Norwegian Bokmål)
huEngine->registerEntry("nb_NO", TXT_KEY_ANR_TITLE, "Applikasjonen svarer ikke");
huEngine->registerEntry("nb_NO", TXT_KEY_ANR_CONTENT, "En applikasjon {title} - {class} svarer ikke.\nHva vil du gjøre med den?");
huEngine->registerEntry("nb_NO", TXT_KEY_ANR_OPTION_TERMINATE, "Avslutt");
huEngine->registerEntry("nb_NO", TXT_KEY_ANR_OPTION_WAIT, "Vent");
huEngine->registerEntry("nb_NO", TXT_KEY_ANR_PROP_UNKNOWN, "(ukjent)");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "En applikasjon <b>{app}</b> ber om en ukjent tillatelse.");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "En applikasjon <b>{app}</b> prøver å fange skjermen din.\n\nVil du tillate den?");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "En applikasjon <b>{app}</b> prøver å laste en plugin: <b>{plugin}</b>.\n\nVil du tillate den?");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Et nytt tastatur er oppdaget: <b>{keyboard}</b>.\n\nVil du tillate at det opererer?");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(ukjent)");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_TITLE, "Tillatelsesforespørsel");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Hint: du kan angi vedvarende regler for disse i Hyprland konfigurasjonsfilen.");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_ALLOW, "Tillat");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Tillat og husk");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_ALLOW_ONCE, "Tillat en gang");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_DENY, "Nekte");
huEngine->registerEntry("nb_NO", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Ukjent applikasjon (wayland client ID {wayland_id})");
huEngine->registerEntry(
"nb_NO", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Ditt XDG_CURRENT_DESKTOP miljø ser ut til å være eksternt administrert, og den nåværende verdien er {value}.\nDette kan forårsake problemer med mindre det er bevisst.");
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_NO_GUIUTILS,
"Ditt system har ikke hyprland-guiutils installert. Dette er en kjøretidsavhengighet for noen dialoger. Vurder å installere den.");
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland kunne ikke laste {count} essensiell ressurs, skyld på distroens pakkeansvarlig for å ha gjort en dårlig jobb med pakkingen!";
return "Hyprland kunne ikke laste {count} essensielle ressurser, skyld på distroens pakkeansvarlig for å ha gjort en dårlig jobb med pakkingen!";
});
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Skjermoppsettet ditt er satt opp feil. Skjerm {name} overlapper med skjerm(er) i oppsettet.\nSjekk wiki (Skjerm oppsett siden) for "
"mer. Dette <b>vil</b> skape problemer.");
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Skjerm {name} feilet å sette de forespurte modusene, faller tilbake til modus {mode}.");
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Ugyldig skala sendt til skjerm {name}: {scale}, bruker foreslått skala: {fixed_scale}");
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Feilet å laste plugin {name}: {error}");
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader omlading feilet, faller tilbake til rgba/rgbx.");
huEngine->registerEntry("nb_NO", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Skjerm {name}: bredt fargespekter er aktivert, men skjermen er ikke i 10-bit modus.");
// nl_NL (Dutch)
huEngine->registerEntry("nl_NL", TXT_KEY_ANR_TITLE, "Applicatie Reageert Niet");
huEngine->registerEntry("nl_NL", TXT_KEY_ANR_CONTENT, "Een applicatie {title} - {class} reageert niet.\nWat wilt u doen?");
huEngine->registerEntry("nl_NL", TXT_KEY_ANR_OPTION_TERMINATE, "Beëindigen");
huEngine->registerEntry("nl_NL", TXT_KEY_ANR_OPTION_WAIT, "Wachten");
huEngine->registerEntry("nl_NL", TXT_KEY_ANR_PROP_UNKNOWN, "(onbekend)");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Een applicatie <b>{app}</b> vraagt om een onbekende machtiging.");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Een applicatie <b>{app}</b> probeert uw scherm op te nemen.\n\nWilt u dit toestaan?");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Een applicatie <b>{app}</b> probeert een plugin te laden: <b>{plugin}</b>.\n\nWilt u dit toestaan?");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_REQUEST_KEYBOARD,
"Een nieuw toetsenbord is gedetecteerd: <b>{keyboard}</b>.\n\nWilt u toestemming geven dat het wordt gebruikt?");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(onbekend)");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_TITLE, "Toestemmingsverzoek");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Tip: U kunt hiervoor vaste regels instellen in het Hyprland-configuratiebestand.");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_ALLOW, "Toestaan");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Toestaan en onthouden");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_ALLOW_ONCE, "Één keer toestaan");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_DENY, "Weigeren");
huEngine->registerEntry("nl_NL", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Onbekende applicatie (wayland client ID {wayland_id})");
huEngine->registerEntry(
"nl_NL", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"De XDG_CURRENT_DESKTOP omgevingsvariabele lijkt extern beheerd te worden en de huidige waarde is {value}.\nDit kan problemen veroorzaken, tenzij dit opzettelijk is.");
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_NO_GUIUTILS,
"Hyprland-guiutils is niet op uw systeem geïnstalleerd. Dit is een runtime-afhankelijkheid voor sommige dialogen. Overweeg het te installeren.");
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland kon {count} essentieel bestand niet laden, geef de pakketbeheerder van uw distro de schuld voor slecht verpakkingswerk!";
return "Hyprland kon {count} essentiële bestanden niet laden, geef de pakketbeheerder van uw distro de schuld voor slecht verpakkingswerk!";
});
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Uw monitorindeling is onjuist ingesteld. Monitor {name} overlapt met één of meerdere andere monitoren in de indeling.\n"
"Zie de wiki (Monitors pagina) voor meer informatie. Dit <b>zal</b> problemen veroorzaken.");
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_MONITOR_MODE_FAIL,
"Monitor {name} is er niet in geslaagd om een van de aangevraagde modi toe te passen en gebruikt nu de modus {mode}.");
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
"Ongeldige schaal opgegeven voor monitor {name}: {scale}, de voorgestelde schaal {fixed_scale} wordt gebruikt.");
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Plugin {name} kon niet worden geladen: {error}");
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Het opnieuw laden van de CM-shader is mislukt. Er wordt teruggevallen op rgba/rgbx.");
huEngine->registerEntry("nl_NL", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: breed kleurbereik is ingeschakeld maar het scherm staat niet in 10-bitmodus.");
// pl_PL (Polish)
huEngine->registerEntry("pl_PL", TXT_KEY_ANR_TITLE, "Aplikacja Nie Odpowiada");
huEngine->registerEntry("pl_PL", TXT_KEY_ANR_CONTENT, "Aplikacja {title} - {class} nie odpowiada.\nCo chcesz z nią zrobić?");
huEngine->registerEntry("pl_PL", TXT_KEY_ANR_OPTION_TERMINATE, "Zakończ proces");
huEngine->registerEntry("pl_PL", TXT_KEY_ANR_OPTION_WAIT, "Czekaj");
huEngine->registerEntry("pl_PL", TXT_KEY_ANR_PROP_UNKNOWN, "(nieznane)");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikacja <b>{app}</b> prosi o pozwolenie na nieznany typ operacji.");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikacja <b>{app}</b> prosi o dostęp do twojego ekranu.\n\nCzy chcesz jej na to pozwolić?");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikacja <b>{app}</b> próbuje załadować plugin: <b>{plugin}</b>.\n\nCzy chcesz jej na to pozwolić?");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Wykryto nową klawiaturę: <b>{keyboard}</b>.\n\nCzy chcesz jej pozwolić operować?");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(nieznane)");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_TITLE, "Prośba o pozwolenie");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Podpowiedź: możesz ustawić stałe zasady w konfiguracji Hyprland'a.");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW, "Zezwól");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Zezwól i zapamiętaj");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_ALLOW_ONCE, "Zezwól raz");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_DENY, "Odmów");
huEngine->registerEntry("pl_PL", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Nieznana aplikacja (ID klienta wayland {wayland_id})");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Zmienna środowiska XDG_CURRENT_DESKTOP została ustawiona zewnętrznie na {value}.\nTo może sprawić problemy, chyba, że jest celowe.");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_NO_GUIUTILS, "Twój system nie ma hyprland-guiutils zainstalowanych, co może sprawić problemy. Zainstaluj pakiet.");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo == 1)
return "Nie udało się załadować {count} kluczowego zasobu, wiń swojego packager'a za robienie słabej roboty!";
return "Nie udało się załadować {count} kluczowych zasobów, wiń swojego packager'a za robienie słabej roboty!";
});
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Pozycje twoich monitorów nie są ustawione poprawnie. Monitor {name} wchodzi na inne monitory.\nWejdź na wiki (stronę Monitory) "
"po więcej. To <b>będzie</b> sprawiać problemy.");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} nie zaakceptował żadnego wybranego programu. Użyto {mode}.");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Nieprawidłowa skala dla monitora {name}: {scale}, użyto proponowanej skali: {fixed_scale}");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nie udało się załadować plugin'a {name}: {error}");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Nie udało się przeładować shader'a CM, użyto rgba/rgbx.");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: skonfigurowano szeroką głębię barw, ale monitor nie jest w trybie 10-bit.");
// zh_CN (Simplified Chinese)
huEngine->registerEntry("zh_CN", TXT_KEY_ANR_TITLE, "应用程序未响应");
huEngine->registerEntry("zh_CN", TXT_KEY_ANR_CONTENT, "应用程序 {title} - {class} 未响应。\n你想要采取什么行动?");
huEngine->registerEntry("zh_CN", TXT_KEY_ANR_OPTION_TERMINATE, "终止");
huEngine->registerEntry("zh_CN", TXT_KEY_ANR_OPTION_WAIT, "等待");
huEngine->registerEntry("zh_CN", TXT_KEY_ANR_PROP_UNKNOWN, "(未知)");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "应用程序 <b>{app}</b> 正在请求一个未知的权限。");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "应用程序 <b>{app}</b> 想要捕获你的屏幕。\n\n允许它这么做吗?");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "应用程序 <b>{app}</b> 想要加载插件: <b>{plugin}</b>。\n\n允许它这么做吗?");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "检测到新的键盘 <b>{keyboard}</b> 接入了。\n\n允许这个键盘操作你的系统吗?");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(未知)");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_TITLE, "权限请求");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "提示你可以在Hyprland配置中为他们创建永久性的规则。");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_ALLOW, "允许");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "总是允许");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_ALLOW_ONCE, "允许一次");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_DENY, "阻止");
huEngine->registerEntry("zh_CN", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "未知的应用程序 Wayland客户端ID {wayland_id}");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"你的环境变量XDG_CURRENT_DESKTOP似乎被外部管理且当前的值为{value}。如果你不是有意这么做,这可能会导致问题。");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_NO_GUIUTILS, "你的系统似乎没有安装hyprland-guiutils。这是一个用于部分对话框的运行时依赖。请考虑安装。");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_FAILED_ASSETS, "Hyprland无法加载{count}个重要资产,问问你发行版的打包者在打包个什么玩意!?");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"你的显示器没有被正确设置。显示器 {name} 和其他显示器的布局重叠了。请看wiki中的“显示器”一章获取更多信息。这<b>会</b>导致问题。");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "显示器 {name} 无法被设置为任何请求的模式,将使用 {mode} 兜底。");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "显示器 {name} 被设置了非法的缩放:{scale},将使用建议的缩放:{fixed_scale}");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "无法加载插件 {name}{error}");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "无法重新加载CM着色器将使用rgba/rgbx兜底。");
huEngine->registerEntry("zh_CN", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "显示器 {name}宽色域被启用了但是显示器并不在10-bit模式。");
// ar (Arabic - Modern Standard)
huEngine->registerEntry("ar", TXT_KEY_ANR_TITLE, "التطبيق لا يستجيب");
huEngine->registerEntry("ar", TXT_KEY_ANR_CONTENT, "التطبيق {title} - {class} لا يستجيب.\nما الذي تريد فعله؟");
huEngine->registerEntry("ar", TXT_KEY_ANR_OPTION_TERMINATE, "إنهاء");
huEngine->registerEntry("ar", TXT_KEY_ANR_OPTION_WAIT, "الانتظار");
huEngine->registerEntry("ar", TXT_KEY_ANR_PROP_UNKNOWN, "(غير معروف)");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "يطلب التطبيق <b>{app}</b> صلاحية غير معروفة.");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "يحاول التطبيق <b>{app}</b> التقاط الشاشة.\n\nهل تريد السماح له بذلك؟");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "يحاول التطبيق <b>{app}</b> تحميل إضافة: <b>{plugin}</b>.\n\nهل تريد السماح له بذلك؟");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "تم اكتشاف لوحة مفاتيح جديدة: <b>{keyboard}</b>.\n\nهل تريد السماح لها بالعمل؟");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(غير معروف)");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_TITLE, "طلب الإذن");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "تلميح: يمكنك تعيين قواعد دائمة لهذه الطلبات في ملف إعدادات Hyprland.");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_ALLOW, "السماح");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "السماح مع تذكّر الاختيار");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_ALLOW_ONCE, "السماح لمرة واحدة");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_DENY, "الرفض");
huEngine->registerEntry("ar", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "تطبيق غير معروف (معرّف عميل Wayland {wayland_id})");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"يبدو أنّ متغيّر البيئة XDG_CURRENT_DESKTOP يُدار من خارج النظام، والقيمة الحالية هي {value}.\n"
"قد يؤدي ذلك إلى مشكلات ما لم يكن مقصودًا.");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_NO_GUIUTILS, "لا يحتوي نظامك على الحزمة hyprland-guiutils مثبتة. هذه حزمة مطلوبة أثناء التشغيل لبعض مربعات الحوار. يُنصَح بتثبيتها.");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "فشل Hyprland في تحميل مورد أساسي ({count}). قد يكون السبب سوء تغليف الحزم في التوزيعة.";
return "فشل Hyprland في تحميل {count} من الموارد الأساسية. قد يكون السبب سوء تغليف الحزم في التوزيعة.";
});
huEngine->registerEntry("ar", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"تم إعداد مخطط الشاشات لديك بشكل غير صحيح. الشاشة {name} تتداخل مع شاشة أو أكثر في المخطط.\n"
"يرجى مراجعة صفحة الشاشات في الويكي لمزيد من التفاصيل. هذا <b>سيسبب</b> مشكلات.");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "فشلت الشاشة {name} في ضبط أي من الأوضاع المطلوبة، وسيتم الرجوع إلى الوضع {mode}.");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "تم تمرير قيمة تحجيم غير صالحة إلى الشاشة {name}: {scale}. سيتم استخدام قيمة التحجيم المقترحة: {fixed_scale}.");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "فشل تحميل الإضافة {name}: {error}");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "فشلت إعادة تحميل نظام إدارة الألوان (CM). سيتم الرجوع إلى صيغة الألوان rgba/rgbx.");
huEngine->registerEntry("ar", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "الشاشة {name}: تم تفعيل نطاق الألوان الواسع، لكن العرض ليس في وضع 10 بت.");
// ru_RU (Russian)
huEngine->registerEntry("ru_RU", TXT_KEY_ANR_TITLE, "Приложение не отвечает");
huEngine->registerEntry("ru_RU", TXT_KEY_ANR_CONTENT, "Приложение {title} - {class} не отвечает.\nЧто вы хотите сделать?");
huEngine->registerEntry("ru_RU", TXT_KEY_ANR_OPTION_TERMINATE, "Завершить");
huEngine->registerEntry("ru_RU", TXT_KEY_ANR_OPTION_WAIT, "Подождать");
huEngine->registerEntry("ru_RU", TXT_KEY_ANR_PROP_UNKNOWN, "(неизвестно)");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Приложение <b>{app}</b> запрашивает неизвестное разрешение.");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Приложение <b>{app}</b> пытается получить доступ к вашему экрану.\n\nРазрешить?");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Приложение <b>{app}</b> пытается загрузить плагин: <b>{plugin}</b>.\n\nРазрешить?");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Обнаружена новая клавиатура: <b>{keyboard}</b>.\n\nРазрешить ей работать?");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(неизвестно)");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_TITLE, "Запрос разрешения");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Подсказка: вы можете настроить постоянные правила для этого в конфигурационном файле Hyprland.");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_ALLOW, "Разрешить");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Разрешить и запомнить");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_ALLOW_ONCE, "Разрешить в этот раз");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_DENY, "Отклонить");
huEngine->registerEntry("ru_RU", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Неизвестное приложение (wayland client ID {wayland_id})");
huEngine->registerEntry(
"ru_RU", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Переменная окружения XDG_CURRENT_DESKTOP установлена извне, текущее значение: {value}.\nЭто может вызвать проблемы, если только это не сделано намеренно.");
huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_NO_GUIUTILS, "Пакет hyprland-guiutils не установлен. Он необходим для некоторых диалогов. Рекомендуется установить его.");
huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Не удалось загрузить {count} критически важный ресурс, пожалуйтесь мейнтейнеру вашего дистрибутива за кривую сборку пакета!";
return "Не удалось загрузить {count} критически важных ресурсов, пожалуйтесь мейнтейнеру вашего дистрибутива за кривую сборку пакета!";
});
huEngine->registerEntry(
"ru_RU", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Неправильно настроен макет мониторов. Монитор {name} перекрывает другие.\nПодробнее см. в документации (страница Monitors). Это <b>обязательно</b> вызовет проблемы.");
huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Монитор {name} не смог установить ни один из запрошенных режимов, выбран режим {mode}.");
huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Недопустимый масштаб для монитора {name}: {scale}, используется предложенный масштаб: {fixed_scale}");
huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Не удалось загрузить плагин {name}: {error}");
huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Не удалось перезагрузить CM shader, используется rgba/rgbx.");
huEngine->registerEntry("ru_RU", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Монитор {name}: расширенный цветовой охват включён, но дисплей не в 10-bit режиме.");
// sr_RS (Serbian)
huEngine->registerEntry("sr_RS", TXT_KEY_ANR_TITLE, "Апликација не реагује");
huEngine->registerEntry("sr_RS", TXT_KEY_ANR_CONTENT, "Апликација {title} - {class} не реагује.\nШта желите да урадите са њом?");
huEngine->registerEntry("sr_RS", TXT_KEY_ANR_OPTION_TERMINATE, "Прекини");
huEngine->registerEntry("sr_RS", TXT_KEY_ANR_OPTION_WAIT, "Чекај");
huEngine->registerEntry("sr_RS", TXT_KEY_ANR_PROP_UNKNOWN, "(непознато)");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Апликација <b>{app}</b> захтева непознату дозволу.");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Апликација <b>{app}</b> покушава да снима твој екран.\n\nДа ли желиш да то дозволиш?");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Апликација <b>{app}</b> покушава да учита додатак: <b>{plugin}</b>.\n\nДа ли желиш да то дозволиш?");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Нова тастатура је детектована: <b>{keyboard}</b>.\n\nДа ли желиш да дозволиш њен рад?");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(непознато)");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_TITLE, "Захтев за дозволу");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Савет: можеш направити трајна правила за ово у Hyprland конфигурационој датотеци.");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_ALLOW, "Дозволи");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Дозволи и запамти");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_ALLOW_ONCE, "Дозволи једном");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_DENY, "Одбиј");
huEngine->registerEntry("sr_RS", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Непозната апликација (wayland client ID {wayland_id})");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Изгледа да се твојим XDG_CURRENT_DESKTOP окружењем управља споља, и тренутна вредност је {value}.\nОво може правити проблеме осим ако је намерно.");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_NO_GUIUTILS,
"Твој систем нема инсталиран hyprland-guiutils. Ово је зависност при покретању за неке дијалоге. Размотри инсталацију.");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland није успео да учита {count} кључни ресурс, криви пакера твоје дистрибуције за лоше одрађен посао!";
if (assetsNo <= 4)
return "Hyprland није успео да учита {count} кључна ресурса, криви пакера твоје дистрибуције за лоше одрађен посао!";
return "Hyprland није успео да учита {count} кључних ресурса, криви пакера твоје дистрибуције за лоше одрађен посао!";
});
huEngine->registerEntry(
"sr_RS", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Твој распоред монитора је неправилно постављен. Монитор {name} се преклапа са другим монитором/мониторима у распореду.\nМолим те погледај вики (Monitors страницу) за "
"више информација. Ово <b>ће</b> изазвати проблеме.");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Монитор {name} није успео да постави ниједан тражени режим, враћање на режим {mode}.");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Невалидна скала прослеђена монитору {name}: {scale}, користи се препоручена скала: {fixed_scale}");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Неуспешно учитавање додатка {name}: {error}");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Поново учитавање CM шејдера није успело, враћање на rgba/rgbx.");
huEngine->registerEntry("sr_RS", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Монитор {name}: широк спектар боја је омогућен али екран није у 10-битном режиму.");
// sr_RS@latin (Serbian Latin)
huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_TITLE, "Aplikacija ne reaguje");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_CONTENT, "Aplikacija {title} - {class} ne reaguje.\nŠta želite da uradite sa njom?");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_OPTION_TERMINATE, "Prekini");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_OPTION_WAIT, "Čekaj");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_ANR_PROP_UNKNOWN, "(nepoznato)");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Aplikacija <b>{app}</b> zahteva nepoznatu dozvolu.");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Aplikacija <b>{app}</b> pokušava da snima tvoj ekran.\n\nDa li želiš da to dozvoliš?");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Aplikacija <b>{app}</b> pokušava da učita dodatak: <b>{plugin}</b>.\n\nDa li želiš da to dozvoliš?");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Nova tastatura je detektovana: <b>{keyboard}</b>.\n\nDa li želiš da dozvoliš njen rad?");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(nepoznato)");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_TITLE, "Zahtev za dozvolu");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "Savet: možeš napraviti trajna pravila za ovo u Hyprland konfiguracionoj datoteci.");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_ALLOW, "Dozvoli");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "Dozvoli i zapamti");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_ALLOW_ONCE, "Dozvoli jednom");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_DENY, "Odbij");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Nepoznata aplikacija (wayland client ID {wayland_id})");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"Izgleda da se tvojim XDG_CURRENT_DESKTOP okruženjem upravlja spolja, i trenutna vrednost je {value}.\nOvo može praviti probleme osim ako je namerno.");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_NO_GUIUTILS,
"Tvoj sistem nema instaliran hyprland-guiutils. Ovo je zavisnost pri pokretanju za neke dijaloge. Razmotri instalaciju.");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_FAILED_ASSETS, [](const Hyprutils::I18n::translationVarMap& vars) {
int assetsNo = std::stoi(vars.at("count"));
if (assetsNo <= 1)
return "Hyprland nije uspeo da učita {count} ključni resurs, krivi pakera tvoje distribucije za loše odrađen posao!";
if (assetsNo <= 4)
return "Hyprland nije uspeo da učita {count} ključna resursa, krivi pakera tvoje distribucije za loše odrađen posao!";
return "Hyprland nije uspeo da učita {count} ključnih resursa, krivi pakera tvoje distribucije za loše odrađen posao!";
});
huEngine->registerEntry(
"sr_RS@latin", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Tvoj raspored monitora je nepravilno postavljen. Monitor {name} se preklapa sa drugim monitorom/monitorima u rasporedu.\nMolim te pogledaj wiki (Monitors stranicu) za "
"više informacija. Ovo <b>će</b> izazvati probleme.");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitor {name} nije uspeo da postavi nijedan traženi režim, vraćanje na režim {mode}.");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Nevalidna skala prosleđena monitoru {name}: {scale}, koristi se preporučena skala: {fixed_scale}");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Neuspešno učitavanje dodatka {name}: {error}");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Ponovno učitavanje CM šejdera nije uspelo, vraćanje na rgba/rgbx.");
huEngine->registerEntry("sr_RS@latin", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: širok spektar boja je omogućen ali ekran nije u 10-bitnom režimu.");
// tr_TR (Turkish)
huEngine->registerEntry("tr_TR", TXT_KEY_ANR_TITLE, "Uygulama Yanıt Vermiyor");
huEngine->registerEntry("tr_TR", TXT_KEY_ANR_CONTENT, "Bir uygulama {title} - {class} yanıt vermiyor.\nBununla ne yapmak istiyorsun?");
huEngine->registerEntry("tr_TR", TXT_KEY_ANR_OPTION_TERMINATE, "Sonlandır");
huEngine->registerEntry("tr_TR", TXT_KEY_ANR_OPTION_WAIT, "Bekle");
huEngine->registerEntry("tr_TR", TXT_KEY_ANR_PROP_UNKNOWN, "(bilinmiyor)");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_UNKNOWN, "Bir uygulama <b>{app}</b> bilinmeyen bir izin istiyor.");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_SCREENCOPY, "Bir uygulama <b>{app}</b> ekran kaydı yapmaya çalışıyor.\n\nİzin vermek istiyor musun?");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_PLUGIN, "Bir uygulama <b>{app}</b> bir eklenti kurmaya çalışıyor: <b>{plugin}</b>.\n\nİzin vermek istiyor musun?");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_REQUEST_KEYBOARD, "Yeni bir klavye algılandı: <b>{keyboard}</b>.\n\nÇalışmasına izin vermek istiyor musun?");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_UNKNOWN_NAME, "(bilinmiyor)");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_TITLE, "İzin isteği");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_PERSISTENCE_HINT, "İpucu: Hyprland config dosyasında bunlar için kalıcı kurallar atayabilirsin.");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_ALLOW, "İzin ver");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER, "İzin ver ve seçimimi hatırla");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_ALLOW_ONCE, "Yalnızca bir defa izin ver");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_DENY, "Reddet");
huEngine->registerEntry("tr_TR", TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP, "Bilinmeyen uygulama (wayland istemci ID {wayland_id})");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
"XDG_CURRENT_DESKTOP ortamın harici olarak yönetiliyor gibi gözüküyor, ve mevcut değeri {value}.\nEğer bu bilinçli değilse sorunlara yol açabilir.");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_NO_GUIUTILS,
"Sisteminde hyprland-guiutils yüklü değil. Bu bazı diyaloglar için bir çalışma zamanı bağımlılığı. İndirmeyi göz önünde bulundurabilirsin.");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_FAILED_ASSETS,
"Hyprland {count} gerekli dosyayı yüklemekte başarısız oldu, kötü bir iş çıkardığı için kullandığın distronun paketleyicisini suçla!");
huEngine->registerEntry(
"tr_TR", TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
"Monitör düzenin yanlış ayarlanmış. Monitör {name} düzenindeki başka monitörlerle çakışıyor.\nLütfen daha fazla bilgi için wiki'ye (Monitörler sayfası) göz at. "
"Bu <b>sorunlara yol açacak</b>.");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_MONITOR_MODE_FAIL, "Monitör {name} istenen modları ayarlamada başarısız oldu, {mode} moduna geri dönülüyor.");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_MONITOR_AUTO_SCALE, "Monitöre geçersiz ölçek iletildi {name}: {scale}, önerilen ölçek kullanılıyor: {fixed_scale}");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "{name} plugini yüklenemedi: {error}");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader yeniden yüklemesi başarısız, rgba/rgbx'e geri dönülüyor.");
huEngine->registerEntry("tr_TR", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitör {name}: wide color gamut etkinleştirildi ama ekran 10-bit modunda değil.");
}
std::string I18n::CI18nEngine::localize(eI18nKeys key, const Hyprutils::I18n::translationVarMap& vars) {
return huEngine->localizeEntry(localeStr, key, vars);
}

50
src/i18n/Engine.hpp Normal file
View file

@ -0,0 +1,50 @@
#pragma once
#include "../helpers/memory/Memory.hpp"
#include <unordered_map>
#include <cstdint>
#include <string>
namespace I18n {
enum eI18nKeys : uint8_t {
TXT_KEY_ANR_TITLE = 0,
TXT_KEY_ANR_CONTENT,
TXT_KEY_ANR_OPTION_TERMINATE,
TXT_KEY_ANR_OPTION_WAIT,
TXT_KEY_ANR_PROP_UNKNOWN,
TXT_KEY_PERMISSION_REQUEST_UNKNOWN,
TXT_KEY_PERMISSION_REQUEST_SCREENCOPY,
TXT_KEY_PERMISSION_REQUEST_PLUGIN,
TXT_KEY_PERMISSION_REQUEST_KEYBOARD,
TXT_KEY_PERMISSION_UNKNOWN_NAME,
TXT_KEY_PERMISSION_TITLE,
TXT_KEY_PERMISSION_PERSISTENCE_HINT,
TXT_KEY_PERMISSION_ALLOW,
TXT_KEY_PERMISSION_ALLOW_AND_REMEMBER,
TXT_KEY_PERMISSION_ALLOW_ONCE,
TXT_KEY_PERMISSION_DENY,
TXT_KEY_PERMISSION_UNKNOWN_WAYLAND_APP,
TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP,
TXT_KEY_NOTIF_NO_GUIUTILS,
TXT_KEY_NOTIF_FAILED_ASSETS,
TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT,
TXT_KEY_NOTIF_MONITOR_MODE_FAIL,
TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN,
TXT_KEY_NOTIF_CM_RELOAD_FAILED,
TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B,
};
class CI18nEngine {
public:
CI18nEngine();
~CI18nEngine() = default;
std::string localize(eI18nKeys key, const std::unordered_map<std::string, std::string>& vars = {});
};
SP<CI18nEngine> i18nEngine();
};

View file

@ -9,51 +9,14 @@
#include "../managers/EventManager.hpp" #include "../managers/EventManager.hpp"
#include "xwayland/XWayland.hpp" #include "xwayland/XWayland.hpp"
SWorkspaceGaps CHyprDwindleLayout::getWorkspaceGaps(const PHLWORKSPACE& pWorkspace) {
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(pWorkspace);
static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
static auto PGAPSOUTDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
auto* const PGAPSIN = sc<CCssGapData*>((PGAPSINDATA.ptr())->getData());
auto* const PGAPSOUT = sc<CCssGapData*>((PGAPSOUTDATA.ptr())->getData());
SWorkspaceGaps gaps;
gaps.in = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
gaps.out = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT);
return gaps;
}
SNodeDisplayEdgeFlags CHyprDwindleLayout::getNodeDisplayEdgeFlags(const CBox& box, const PHLMONITOR& monitor) {
return {
.top = STICKS(box.y, monitor->m_position.y + monitor->m_reservedTopLeft.y),
.bottom = STICKS(box.y + box.h, monitor->m_position.y + monitor->m_size.y - monitor->m_reservedBottomRight.y),
.left = STICKS(box.x, monitor->m_position.x + monitor->m_reservedTopLeft.x),
.right = STICKS(box.x + box.w, monitor->m_position.x + monitor->m_size.x - monitor->m_reservedBottomRight.x),
};
}
void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverride, bool verticalOverride) { void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverride, bool verticalOverride) {
if (children[0]) { if (children[0]) {
static auto PSMARTSPLIT = CConfigValue<Hyprlang::INT>("dwindle:smart_split"); static auto PSMARTSPLIT = CConfigValue<Hyprlang::INT>("dwindle:smart_split");
static auto PPRESERVESPLIT = CConfigValue<Hyprlang::INT>("dwindle:preserve_split"); static auto PPRESERVESPLIT = CConfigValue<Hyprlang::INT>("dwindle:preserve_split");
static auto PFLMULT = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier"); static auto PFLMULT = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier");
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceID);
if (!PWORKSPACE)
return;
const auto PMONITOR = PWORKSPACE->m_monitor.lock();
if (!PMONITOR)
return;
const auto edges = layout->getNodeDisplayEdgeFlags(box, PMONITOR);
auto [gapsIn, gapsOut] = layout->getWorkspaceGaps(PWORKSPACE);
const Vector2D availableSize = box.size() -
Vector2D{(edges.left ? gapsOut.m_left : gapsIn.m_left / 2.f) + (edges.right ? gapsOut.m_right : gapsIn.m_right / 2.f),
(edges.top ? gapsOut.m_top : gapsIn.m_top / 2.f) + (edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom / 2.f)};
if (*PPRESERVESPLIT == 0 && *PSMARTSPLIT == 0) if (*PPRESERVESPLIT == 0 && *PSMARTSPLIT == 0)
splitTop = availableSize.y * *PFLMULT > availableSize.x; splitTop = box.h * *PFLMULT > box.w;
if (verticalOverride) if (verticalOverride)
splitTop = true; splitTop = true;
@ -64,26 +27,12 @@ void SDwindleNodeData::recalcSizePosRecursive(bool force, bool horizontalOverrid
if (SPLITSIDE) { if (SPLITSIDE) {
// split left/right // split left/right
const float gapsAppliedToChild1 = (edges.left ? gapsOut.m_left : gapsIn.m_left / 2.f) + gapsIn.m_right / 2.f; const float FIRSTSIZE = box.w / 2.0 * splitRatio;
const float gapsAppliedToChild2 = gapsIn.m_left / 2.f + (edges.right ? gapsOut.m_right : gapsIn.m_right / 2.f);
const float totalGaps = gapsAppliedToChild1 + gapsAppliedToChild2;
const float totalAvailable = box.w - totalGaps;
const float child1Available = totalAvailable * (splitRatio / 2.f);
const float FIRSTSIZE = child1Available + gapsAppliedToChild1;
children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize(); children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize();
children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize(); children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize();
} else { } else {
// split top/bottom // split top/bottom
const float gapsAppliedToChild1 = (edges.top ? gapsOut.m_top : gapsIn.m_top / 2.f) + gapsIn.m_bottom / 2.f; const float FIRSTSIZE = box.h / 2.0 * splitRatio;
const float gapsAppliedToChild2 = gapsIn.m_top / 2.f + (edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom / 2.f);
const float totalGaps = gapsAppliedToChild1 + gapsAppliedToChild2;
const float totalAvailable = box.h - totalGaps;
const float child1Available = totalAvailable * (splitRatio / 2.f);
const float FIRSTSIZE = child1Available + gapsAppliedToChild1;
children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize(); children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize();
children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize(); children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize();
} }
@ -167,7 +116,10 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
} }
// for gaps outer // for gaps outer
const auto edges = getNodeDisplayEdgeFlags(pNode->box, PMONITOR); const bool DISPLAYLEFT = STICKS(pNode->box.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x);
const bool DISPLAYRIGHT = STICKS(pNode->box.x + pNode->box.w, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x);
const bool DISPLAYTOP = STICKS(pNode->box.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y);
const bool DISPLAYBOTTOM = STICKS(pNode->box.y + pNode->box.h, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y);
const auto PWINDOW = pNode->pWindow.lock(); const auto PWINDOW = pNode->pWindow.lock();
// get specific gaps and rules for this workspace, // get specific gaps and rules for this workspace,
@ -183,7 +135,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks) if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
return; return;
PWINDOW->unsetWindowData(PRIORITY_LAYOUT); PWINDOW->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
PWINDOW->updateWindowData(); PWINDOW->updateWindowData();
static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in"); static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
@ -228,9 +180,9 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
} }
} }
const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(edges.left ? gapsOut.m_left : gapsIn.m_left), sc<double>(edges.top ? gapsOut.m_top : gapsIn.m_top)); const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(DISPLAYLEFT ? gapsOut.m_left : gapsIn.m_left), sc<double>(DISPLAYTOP ? gapsOut.m_top : gapsIn.m_top));
const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc<double>(edges.right ? gapsOut.m_right : gapsIn.m_right), sc<double>(edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom)); const auto GAPOFFSETBOTTOMRIGHT = Vector2D(sc<double>(DISPLAYRIGHT ? gapsOut.m_right : gapsIn.m_right), sc<double>(DISPLAYBOTTOM ? gapsOut.m_bottom : gapsIn.m_bottom));
calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2; calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2;
calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding; calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding;
@ -272,9 +224,9 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight - Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight -
Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize}; Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize};
Vector2D minSize = PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable); Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
Vector2D maxSize = Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} :
PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable); PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
calcSize = calcSize.clamp(minSize, maxSize); calcSize = calcSize.clamp(minSize, maxSize);
calcPos += (availableSpace - calcSize) / 2.0; calcPos += (availableSpace - calcSize) / 2.0;
@ -398,6 +350,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir
} }
// get the node under our cursor // get the node under our cursor
m_dwindleNodesData.emplace_back(); m_dwindleNodesData.emplace_back();
const auto NEWPARENT = &m_dwindleNodesData.back(); const auto NEWPARENT = &m_dwindleNodesData.back();
@ -410,17 +363,8 @@ void CHyprDwindleLayout::onWindowCreatedTiling(PHLWINDOW pWindow, eDirection dir
static auto PWIDTHMULTIPLIER = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier"); static auto PWIDTHMULTIPLIER = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier");
const auto edges = getNodeDisplayEdgeFlags(NEWPARENT->box, PMONITOR);
const auto WORKSPACE = g_pCompositor->getWorkspaceByID(PNODE->workspaceID);
auto [gapsIn, gapsOut] = getWorkspaceGaps(WORKSPACE);
// if cursor over first child, make it first, etc // if cursor over first child, make it first, etc
const Vector2D availableSize = NEWPARENT->box.size() - const auto SIDEBYSIDE = NEWPARENT->box.w > NEWPARENT->box.h * *PWIDTHMULTIPLIER;
Vector2D{(edges.left ? gapsOut.m_left : gapsIn.m_left / 2.f) + (edges.right ? gapsOut.m_right : gapsIn.m_right / 2.f),
(edges.top ? gapsOut.m_top : gapsIn.m_top / 2.f) + (edges.bottom ? gapsOut.m_bottom : gapsIn.m_bottom / 2.f)};
const auto SIDEBYSIDE = availableSize.x > availableSize.y * *PWIDTHMULTIPLIER;
NEWPARENT->splitTop = !SIDEBYSIDE; NEWPARENT->splitTop = !SIDEBYSIDE;
static auto PFORCESPLIT = CConfigValue<Hyprlang::INT>("dwindle:force_split"); static auto PFORCESPLIT = CConfigValue<Hyprlang::INT>("dwindle:force_split");
@ -547,7 +491,7 @@ void CHyprDwindleLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
return; return;
} }
pWindow->unsetWindowData(PRIORITY_LAYOUT); pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData(); pWindow->updateWindowData();
if (pWindow->isFullscreen()) if (pWindow->isFullscreen())
@ -601,6 +545,8 @@ void CHyprDwindleLayout::recalculateMonitor(const MONITORID& monid) {
#ifndef NO_XWAYLAND #ifndef NO_XWAYLAND
CBox box = g_pCompositor->calculateX11WorkArea(); CBox box = g_pCompositor->calculateX11WorkArea();
if (!g_pXWayland || !g_pXWayland->m_wm)
return;
g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h); g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h);
#endif #endif
} }
@ -662,9 +608,9 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
const auto PNODE = getNodeFromWindow(PWINDOW); const auto PNODE = getNodeFromWindow(PWINDOW);
if (!PNODE) { if (!PNODE) {
*PWINDOW->m_realSize = *PWINDOW->m_realSize = (PWINDOW->m_realSize->goal() + pixResize)
(PWINDOW->m_realSize->goal() + pixResize) .clamp(PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}),
.clamp(PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY})); PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}));
PWINDOW->updateWindowDecos(); PWINDOW->updateWindowDecos();
return; return;
} }
@ -674,7 +620,10 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
// get some data about our window // get some data about our window
const auto PMONITOR = PWINDOW->m_monitor.lock(); const auto PMONITOR = PWINDOW->m_monitor.lock();
const auto edges = getNodeDisplayEdgeFlags(CBox{PWINDOW->m_position, PWINDOW->m_size}, PMONITOR); const bool DISPLAYLEFT = STICKS(PWINDOW->m_position.x, PMONITOR->m_position.x + PMONITOR->m_reservedTopLeft.x);
const bool DISPLAYRIGHT = STICKS(PWINDOW->m_position.x + PWINDOW->m_size.x, PMONITOR->m_position.x + PMONITOR->m_size.x - PMONITOR->m_reservedBottomRight.x);
const bool DISPLAYTOP = STICKS(PWINDOW->m_position.y, PMONITOR->m_position.y + PMONITOR->m_reservedTopLeft.y);
const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_position.y + PWINDOW->m_size.y, PMONITOR->m_position.y + PMONITOR->m_size.y - PMONITOR->m_reservedBottomRight.y);
if (PWINDOW->m_isPseudotiled) { if (PWINDOW->m_isPseudotiled) {
if (!m_pseudoDragFlags.started) { if (!m_pseudoDragFlags.started) {
@ -707,8 +656,8 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
CBox wbox = PNODE->box; CBox wbox = PNODE->box;
wbox.round(); wbox.round();
Vector2D minSize = PWINDOW->m_windowData.minSize.valueOr(Vector2D{30.0, 30.0}); Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{30.0, 30.0});
Vector2D maxSize = PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY}); Vector2D maxSize = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY});
Vector2D upperBound = Vector2D{std::min(maxSize.x, wbox.w), std::min(maxSize.y, wbox.h)}; Vector2D upperBound = Vector2D{std::min(maxSize.x, wbox.w), std::min(maxSize.y, wbox.h)};
PWINDOW->m_pseudoSize = PWINDOW->m_pseudoSize.clamp(minSize, upperBound); PWINDOW->m_pseudoSize = PWINDOW->m_pseudoSize.clamp(minSize, upperBound);
@ -722,10 +671,10 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
// construct allowed movement // construct allowed movement
Vector2D allowedMovement = pixResize; Vector2D allowedMovement = pixResize;
if (edges.left && edges.right) if (DISPLAYLEFT && DISPLAYRIGHT)
allowedMovement.x = 0; allowedMovement.x = 0;
if (edges.bottom && edges.top) if (DISPLAYBOTTOM && DISPLAYTOP)
allowedMovement.y = 0; allowedMovement.y = 0;
if (*PSMARTRESIZING == 1) { if (*PSMARTRESIZING == 1) {
@ -735,10 +684,10 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorn
SDwindleNodeData* PHOUTER = nullptr; SDwindleNodeData* PHOUTER = nullptr;
SDwindleNodeData* PHINNER = nullptr; SDwindleNodeData* PHINNER = nullptr;
const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || edges.right; const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || DISPLAYRIGHT;
const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || edges.bottom; const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || DISPLAYBOTTOM;
const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || edges.left; const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || DISPLAYLEFT;
const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || edges.top; const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || DISPLAYTOP;
const auto NONE = corner == CORNER_NONE; const auto NONE = corner == CORNER_NONE;
for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent) { for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent) {
@ -868,7 +817,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFu
*pWindow->m_realPosition = pWindow->m_lastFloatingPosition; *pWindow->m_realPosition = pWindow->m_lastFloatingPosition;
*pWindow->m_realSize = pWindow->m_lastFloatingSize; *pWindow->m_realSize = pWindow->m_lastFloatingSize;
pWindow->unsetWindowData(PRIORITY_LAYOUT); pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData(); pWindow->updateWindowData();
} }
} else { } else {

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "IHyprLayout.hpp" #include "IHyprLayout.hpp"
#include "../config/ConfigDataValues.hpp"
#include "../desktop/DesktopTypes.hpp" #include "../desktop/DesktopTypes.hpp"
#include <list> #include <list>
@ -13,15 +12,6 @@
class CHyprDwindleLayout; class CHyprDwindleLayout;
enum eFullscreenMode : int8_t; enum eFullscreenMode : int8_t;
struct SNodeDisplayEdgeFlags {
bool top = false, bottom = false, left = false, right = false;
};
struct SWorkspaceGaps {
CCssGapData in;
CCssGapData out;
};
struct SDwindleNodeData { struct SDwindleNodeData {
SDwindleNodeData* pParent = nullptr; SDwindleNodeData* pParent = nullptr;
bool isNode = false; bool isNode = false;
@ -75,9 +65,6 @@ class CHyprDwindleLayout : public IHyprLayout {
virtual void onDisable(); virtual void onDisable();
private: private:
SWorkspaceGaps getWorkspaceGaps(const PHLWORKSPACE& pWorkspace);
SNodeDisplayEdgeFlags getNodeDisplayEdgeFlags(const CBox& box, const PHLMONITOR& monitor);
std::list<SDwindleNodeData> m_dwindleNodesData; std::list<SDwindleNodeData> m_dwindleNodesData;
struct { struct {

View file

@ -13,11 +13,12 @@
#include "../managers/EventManager.hpp" #include "../managers/EventManager.hpp"
#include "../managers/HookSystemManager.hpp" #include "../managers/HookSystemManager.hpp"
#include "../managers/cursor/CursorShapeOverrideController.hpp" #include "../managers/cursor/CursorShapeOverrideController.hpp"
#include "../desktop/rule/windowRule/WindowRule.hpp"
void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) { void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) {
CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow); CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(pWindow);
const bool HASPERSISTENTSIZE = std::ranges::any_of(pWindow->m_matchedRules, [](const auto& rule) { return rule->m_ruleType == CWindowRule::RULE_PERSISTENTSIZE; }); const bool HASPERSISTENTSIZE = pWindow->m_ruleApplicator->persistentSize().valueOrDefault();
const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt; const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt;
@ -77,7 +78,7 @@ void IHyprLayout::onWindowRemoved(PHLWINDOW pWindow) {
pWindow->updateWindowDecos(); pWindow->updateWindowDecos();
PWINDOWPREV->getGroupCurrent()->updateWindowDecos(); PWINDOWPREV->getGroupCurrent()->updateWindowDecos();
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow); pWindow->updateDecorationValues();
return; return;
} }
@ -637,10 +638,10 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
} else if (g_pInputManager->m_dragMode == MBIND_RESIZE || g_pInputManager->m_dragMode == MBIND_RESIZE_FORCE_RATIO || g_pInputManager->m_dragMode == MBIND_RESIZE_BLOCK_RATIO) { } else if (g_pInputManager->m_dragMode == MBIND_RESIZE || g_pInputManager->m_dragMode == MBIND_RESIZE_FORCE_RATIO || g_pInputManager->m_dragMode == MBIND_RESIZE_BLOCK_RATIO) {
if (DRAGGINGWINDOW->m_isFloating) { if (DRAGGINGWINDOW->m_isFloating) {
Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE))); Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)));
Vector2D MAXSIZE; Vector2D MAXSIZE;
if (DRAGGINGWINDOW->m_windowData.maxSize.hasValue()) if (DRAGGINGWINDOW->m_ruleApplicator->maxSize().hasValue())
MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, DRAGGINGWINDOW->m_windowData.maxSize.value()); MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, DRAGGINGWINDOW->m_ruleApplicator->maxSize().value());
else else
MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, Vector2D(std::numeric_limits<double>::max(), std::numeric_limits<double>::max())); MAXSIZE = DRAGGINGWINDOW->requestedMaxSize().clamp({}, Vector2D(std::numeric_limits<double>::max(), std::numeric_limits<double>::max()));
@ -657,7 +658,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
newSize = newSize + Vector2D(-DELTA.x, DELTA.y); newSize = newSize + Vector2D(-DELTA.x, DELTA.y);
eMouseBindMode mode = g_pInputManager->m_dragMode; eMouseBindMode mode = g_pInputManager->m_dragMode;
if (DRAGGINGWINDOW->m_windowData.keepAspectRatio.valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO) if (DRAGGINGWINDOW->m_ruleApplicator->keepAspectRatio().valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO)
mode = MBIND_RESIZE_FORCE_RATIO; mode = MBIND_RESIZE_FORCE_RATIO;
if (m_beginDragSizeXY.x >= 1 && m_beginDragSizeXY.y >= 1 && mode == MBIND_RESIZE_FORCE_RATIO) { if (m_beginDragSizeXY.x >= 1 && m_beginDragSizeXY.y >= 1 && mode == MBIND_RESIZE_FORCE_RATIO) {
@ -803,14 +804,15 @@ void IHyprLayout::changeWindowFloatingMode(PHLWINDOW pWindow) {
g_pHyprRenderer->damageMonitor(pWindow->m_monitor.lock()); g_pHyprRenderer->damageMonitor(pWindow->m_monitor.lock());
pWindow->unsetWindowData(PRIORITY_LAYOUT); pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData(); pWindow->updateWindowData();
if (pWindow == m_lastTiledWindow) if (pWindow == m_lastTiledWindow)
m_lastTiledWindow.reset(); m_lastTiledWindow.reset();
} }
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow); pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE | Desktop::Rule::RULE_PROP_FLOATING);
pWindow->updateDecorationValues();
pWindow->updateToplevel(); pWindow->updateToplevel();
g_pHyprRenderer->damageWindow(pWindow); g_pHyprRenderer->damageWindow(pWindow);
} }
@ -826,19 +828,20 @@ void IHyprLayout::fitFloatingWindowOnMonitor(PHLWINDOW w, std::optional<CBox> tb
const auto EXTENTS = w->getWindowExtentsUnified(RESERVED_EXTENTS | INPUT_EXTENTS); const auto EXTENTS = w->getWindowExtentsUnified(RESERVED_EXTENTS | INPUT_EXTENTS);
CBox targetBoxMonLocal = tb.value_or(w->getWindowMainSurfaceBox()).translate(-PMONITOR->m_position).addExtents(EXTENTS); CBox targetBoxMonLocal = tb.value_or(w->getWindowMainSurfaceBox()).translate(-PMONITOR->m_position).addExtents(EXTENTS);
const auto MONITOR_LOCAL_BOX = PMONITOR->logicalBoxMinusExtents().translate(-PMONITOR->m_position);
if (targetBoxMonLocal.w < PMONITOR->m_size.x) { if (targetBoxMonLocal.w < MONITOR_LOCAL_BOX.w) {
if (targetBoxMonLocal.x < 0) if (targetBoxMonLocal.x < MONITOR_LOCAL_BOX.x)
targetBoxMonLocal.x = 0; targetBoxMonLocal.x = MONITOR_LOCAL_BOX.x;
else if (targetBoxMonLocal.x + targetBoxMonLocal.w > PMONITOR->m_size.x) else if (targetBoxMonLocal.x + targetBoxMonLocal.w > MONITOR_LOCAL_BOX.w)
targetBoxMonLocal.x = PMONITOR->m_size.x - targetBoxMonLocal.w; targetBoxMonLocal.x = MONITOR_LOCAL_BOX.w - targetBoxMonLocal.w;
} }
if (targetBoxMonLocal.h < PMONITOR->m_size.y) { if (targetBoxMonLocal.h < MONITOR_LOCAL_BOX.h) {
if (targetBoxMonLocal.y < 0) if (targetBoxMonLocal.y < MONITOR_LOCAL_BOX.y)
targetBoxMonLocal.y = 0; targetBoxMonLocal.y = MONITOR_LOCAL_BOX.y;
else if (targetBoxMonLocal.y + targetBoxMonLocal.h > PMONITOR->m_size.y) else if (targetBoxMonLocal.y + targetBoxMonLocal.h > MONITOR_LOCAL_BOX.h)
targetBoxMonLocal.y = PMONITOR->m_size.y - targetBoxMonLocal.h; targetBoxMonLocal.y = MONITOR_LOCAL_BOX.h - targetBoxMonLocal.h;
} }
*w->m_realPosition = (targetBoxMonLocal.pos() + PMONITOR->m_position + EXTENTS.topLeft).round(); *w->m_realPosition = (targetBoxMonLocal.pos() + PMONITOR->m_position + EXTENTS.topLeft).round();
@ -884,7 +887,7 @@ PHLWINDOW IHyprLayout::getNextWindowCandidate(PHLWINDOW pWindow) {
// find whether there is a floating window below this one // find whether there is a floating window below this one
for (auto const& w : g_pCompositor->m_windows) { for (auto const& w : g_pCompositor->m_windows) {
if (w->m_isMapped && !w->isHidden() && w->m_isFloating && !w->isX11OverrideRedirect() && w->m_workspace == pWindow->m_workspace && !w->m_X11ShouldntFocus && if (w->m_isMapped && !w->isHidden() && w->m_isFloating && !w->isX11OverrideRedirect() && w->m_workspace == pWindow->m_workspace && !w->m_X11ShouldntFocus &&
!w->m_windowData.noFocus.valueOrDefault() && w != pWindow) { !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pWindow) {
if (VECINRECT((pWindow->m_size / 2.f + pWindow->m_position), w->m_position.x, w->m_position.y, w->m_position.x + w->m_size.x, w->m_position.y + w->m_size.y)) { if (VECINRECT((pWindow->m_size / 2.f + pWindow->m_position), w->m_position.x, w->m_position.y, w->m_position.x + w->m_size.x, w->m_position.y + w->m_size.y)) {
return w; return w;
} }
@ -903,7 +906,7 @@ PHLWINDOW IHyprLayout::getNextWindowCandidate(PHLWINDOW pWindow) {
// if not, floating window // if not, floating window
for (auto const& w : g_pCompositor->m_windows) { for (auto const& w : g_pCompositor->m_windows) {
if (w->m_isMapped && !w->isHidden() && w->m_isFloating && !w->isX11OverrideRedirect() && w->m_workspace == pWindow->m_workspace && !w->m_X11ShouldntFocus && if (w->m_isMapped && !w->isHidden() && w->m_isFloating && !w->isX11OverrideRedirect() && w->m_workspace == pWindow->m_workspace && !w->m_X11ShouldntFocus &&
!w->m_windowData.noFocus.valueOrDefault() && w != pWindow) !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pWindow)
return w; return w;
} }
@ -952,7 +955,7 @@ Vector2D IHyprLayout::predictSizeForNewWindowFloating(PHLWINDOW pWindow) { // ge
if (g_pCompositor->m_lastMonitor) { if (g_pCompositor->m_lastMonitor) {
// If `persistentsize` is set, use the stored size if available. // If `persistentsize` is set, use the stored size if available.
const bool HASPERSISTENTSIZE = std::ranges::any_of(pWindow->m_matchedRules, [](const auto& rule) { return rule->m_ruleType == CWindowRule::RULE_PERSISTENTSIZE; }); const bool HASPERSISTENTSIZE = pWindow->m_ruleApplicator->persistentSize().valueOrDefault();
const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt; const auto STOREDSIZE = HASPERSISTENTSIZE ? g_pConfigManager->getStoredFloatingSize(pWindow) : std::nullopt;
@ -961,27 +964,10 @@ Vector2D IHyprLayout::predictSizeForNewWindowFloating(PHLWINDOW pWindow) { // ge
return STOREDSIZE.value(); return STOREDSIZE.value();
} }
for (auto const& r : g_pConfigManager->getMatchingRules(pWindow, true, true)) { if (!pWindow->m_ruleApplicator->static_.size.empty()) {
if (r->m_ruleType != CWindowRule::RULE_SIZE) const auto SIZE = Desktop::Rule::parseRelativeVector(pWindow, pWindow->m_ruleApplicator->static_.size);
continue; if (SIZE)
return SIZE.value();
try {
const auto VALUE = r->m_rule.substr(r->m_rule.find(' ') + 1);
const auto SIZEXSTR = VALUE.substr(0, VALUE.find(' '));
const auto SIZEYSTR = VALUE.substr(VALUE.find(' ') + 1);
const auto MAXSIZE = pWindow->requestedMaxSize();
const float SIZEX = SIZEXSTR == "max" ? std::clamp(MAXSIZE.x, MIN_WINDOW_SIZE, g_pCompositor->m_lastMonitor->m_size.x) :
stringToPercentage(SIZEXSTR, g_pCompositor->m_lastMonitor->m_size.x);
const float SIZEY = SIZEYSTR == "max" ? std::clamp(MAXSIZE.y, MIN_WINDOW_SIZE, g_pCompositor->m_lastMonitor->m_size.y) :
stringToPercentage(SIZEYSTR, g_pCompositor->m_lastMonitor->m_size.y);
sizeOverride = {SIZEX, SIZEY};
} catch (...) { Debug::log(LOG, "Rule size failed, rule: {} -> {}", r->m_rule, r->m_value); }
break;
} }
} }
@ -989,17 +975,7 @@ Vector2D IHyprLayout::predictSizeForNewWindowFloating(PHLWINDOW pWindow) { // ge
} }
Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) { Vector2D IHyprLayout::predictSizeForNewWindow(PHLWINDOW pWindow) {
bool shouldBeFloated = g_pXWaylandManager->shouldBeFloated(pWindow, true); bool shouldBeFloated = g_pXWaylandManager->shouldBeFloated(pWindow, true) || pWindow->m_ruleApplicator->static_.floating.value_or(false);
if (!shouldBeFloated) {
for (auto const& r : g_pConfigManager->getMatchingRules(pWindow, true, true)) {
if (r->m_ruleType != CWindowRule::RULE_FLOAT)
continue;
shouldBeFloated = true;
break;
}
}
Vector2D sizePredicted = {}; Vector2D sizePredicted = {};
@ -1042,7 +1018,7 @@ bool IHyprLayout::updateDragWindow() {
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal(); const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
*DRAGGINGWINDOW->m_realPosition = MOUSECOORDS - DRAGGINGWINDOW->m_realSize->goal() / 2.f; *DRAGGINGWINDOW->m_realPosition = MOUSECOORDS - DRAGGINGWINDOW->m_realSize->goal() / 2.f;
} else if (!DRAGGINGWINDOW->m_isFloating && g_pInputManager->m_dragMode == MBIND_MOVE) { } else if (!DRAGGINGWINDOW->m_isFloating && g_pInputManager->m_dragMode == MBIND_MOVE) {
Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE))); Vector2D MINSIZE = DRAGGINGWINDOW->requestedMinSize().clamp(DRAGGINGWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE)));
DRAGGINGWINDOW->m_lastFloatingSize = (DRAGGINGWINDOW->m_realSize->goal() * 0.8489).clamp(MINSIZE, Vector2D{}).floor(); DRAGGINGWINDOW->m_lastFloatingSize = (DRAGGINGWINDOW->m_realSize->goal() * 0.8489).clamp(MINSIZE, Vector2D{}).floor();
*DRAGGINGWINDOW->m_realPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_realSize->goal() / 2.f; *DRAGGINGWINDOW->m_realPosition = g_pInputManager->getMouseCoordsInternal() - DRAGGINGWINDOW->m_realSize->goal() / 2.f;
if (g_pInputManager->m_dragThresholdReached) { if (g_pInputManager->m_dragThresholdReached) {

View file

@ -242,7 +242,7 @@ void CHyprMasterLayout::onWindowRemovedTiling(PHLWINDOW pWindow) {
const auto MASTERSLEFT = getMastersOnWorkspace(WORKSPACEID); const auto MASTERSLEFT = getMastersOnWorkspace(WORKSPACEID);
static auto SMALLSPLIT = CConfigValue<Hyprlang::INT>("master:allow_small_split"); static auto SMALLSPLIT = CConfigValue<Hyprlang::INT>("master:allow_small_split");
pWindow->unsetWindowData(PRIORITY_LAYOUT); pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData(); pWindow->updateWindowData();
if (pWindow->isFullscreen()) if (pWindow->isFullscreen())
@ -297,6 +297,8 @@ void CHyprMasterLayout::recalculateMonitor(const MONITORID& monid) {
#ifndef NO_XWAYLAND #ifndef NO_XWAYLAND
CBox box = g_pCompositor->calculateX11WorkArea(); CBox box = g_pCompositor->calculateX11WorkArea();
if (!g_pXWayland || !g_pXWayland->m_wm)
return;
g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h); g_pXWayland->m_wm->updateWorkArea(box.x, box.y, box.w, box.h);
#endif #endif
} }
@ -661,7 +663,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks) if (PWINDOW->isFullscreen() && !pNode->ignoreFullscreenChecks)
return; return;
PWINDOW->unsetWindowData(PRIORITY_LAYOUT); PWINDOW->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
PWINDOW->updateWindowData(); PWINDOW->updateWindowData();
static auto PANIMATE = CConfigValue<Hyprlang::INT>("misc:animate_manual_resizes"); static auto PANIMATE = CConfigValue<Hyprlang::INT>("misc:animate_manual_resizes");
@ -706,9 +708,9 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight - Vector2D monitorAvailable = PMONITOR->m_size - PMONITOR->m_reservedTopLeft - PMONITOR->m_reservedBottomRight -
Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize}; Vector2D{(double)(gapsOut.m_left + gapsOut.m_right), (double)(gapsOut.m_top + gapsOut.m_bottom)} - Vector2D{2.0 * borderSize, 2.0 * borderSize};
Vector2D minSize = PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable); Vector2D minSize = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
Vector2D maxSize = Vector2D maxSize = PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} :
PWINDOW->isFullscreen() ? Vector2D{INFINITY, INFINITY} : PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable); PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
calcSize = calcSize.clamp(minSize, maxSize); calcSize = calcSize.clamp(minSize, maxSize);
calcPos += (availableSpace - calcSize) / 2.0; calcPos += (availableSpace - calcSize) / 2.0;
@ -760,9 +762,9 @@ void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, eRectCorne
const auto PNODE = getNodeFromWindow(PWINDOW); const auto PNODE = getNodeFromWindow(PWINDOW);
if (!PNODE) { if (!PNODE) {
*PWINDOW->m_realSize = *PWINDOW->m_realSize = (PWINDOW->m_realSize->goal() + pixResize)
(PWINDOW->m_realSize->goal() + pixResize) .clamp(PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}),
.clamp(PWINDOW->m_windowData.minSize.valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}), PWINDOW->m_windowData.maxSize.valueOr(Vector2D{INFINITY, INFINITY})); PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}));
PWINDOW->updateWindowDecos(); PWINDOW->updateWindowDecos();
return; return;
} }
@ -917,7 +919,7 @@ void CHyprMasterLayout::fullscreenRequestForWindow(PHLWINDOW pWindow, const eFul
*pWindow->m_realPosition = pWindow->m_lastFloatingPosition; *pWindow->m_realPosition = pWindow->m_lastFloatingPosition;
*pWindow->m_realSize = pWindow->m_lastFloatingSize; *pWindow->m_realSize = pWindow->m_lastFloatingSize;
pWindow->unsetWindowData(PRIORITY_LAYOUT); pWindow->m_ruleApplicator->resetProps(Desktop::Rule::RULE_PROP_ALL, Desktop::Types::PRIORITY_LAYOUT);
pWindow->updateWindowData(); pWindow->updateWindowData();
} }
} else { } else {

Some files were not shown because too many files have changed in this diff Show more