diff --git a/.github/actions/setup_base/action.yml b/.github/actions/setup_base/action.yml
index d7b52a791..665d7f07d 100644
--- a/.github/actions/setup_base/action.yml
+++ b/.github/actions/setup_base/action.yml
@@ -45,6 +45,7 @@ runs:
libxkbfile \
lld \
meson \
+ muparser \
ninja \
pango \
pixman \
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index d9c0d1a6c..d14ac02e9 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -21,7 +21,7 @@ jobs:
- name: Build Hyprland
run: |
- CFLAGS=-Werror CXXFLAGS=-Werror make all
+ CFLAGS=-Werror CXXFLAGS=-Werror make nopch
- name: Compress and package artifacts
run: |
@@ -41,86 +41,16 @@ jobs:
name: Build archive
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:
permissions: read-all
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
- name: "Code Style (Arch)"
+ name: "Code Style"
runs-on: ubuntu-latest
- container:
- image: archlinux
steps:
- - name: Checkout repository actions
+ - name: Checkout repository
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
- run: ninja -C build clang-format-check
+ uses: jidicula/clang-format-action@v4.16.0
+ with:
+ exclude-regex: ^subprojects$
diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml
index e935a605b..505829e36 100644
--- a/.github/workflows/clang-format.yml
+++ b/.github/workflows/clang-format.yml
@@ -4,43 +4,23 @@ jobs:
clang-format:
permissions: write-all
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
- name: "Code Style (Arch)"
+ name: "Code Style"
runs-on: ubuntu-latest
- container:
- image: archlinux
steps:
- - name: Checkout repository actions
+ - name: Checkout repository
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
- run: ninja -C build clang-format-check
+ uses: jidicula/clang-format-action@v4.16.0
+ with:
+ exclude-regex: ^subprojects$
- - name: clang-format apply
- if: ${{ failure() && github.event_name == 'pull_request' }}
- run: ninja -C build clang-format
-
- - name: Create patch
+ - name: Create comment
if: ${{ failure() && github.event_name == 'pull_request' }}
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 '' >> clang-format.patch
- echo 'clang-format.patch
' >> 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 ' ' >> 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
- - name: Comment patch
+ - name: Post comment
if: ${{ failure() && github.event_name == 'pull_request' }}
uses: mshick/add-pr-comment@v2
with:
diff --git a/.github/workflows/nix-test.yml b/.github/workflows/nix-test.yml
index 086f0077c..68357093a 100644
--- a/.github/workflows/nix-test.yml
+++ b/.github/workflows/nix-test.yml
@@ -20,25 +20,13 @@ jobs:
- name: Restore and save Nix store
uses: nix-community/cache-nix-action@v6
with:
- # restore and save a cache using this key
- primary-key: nix-${{ runner.os }}
+ # restore and save a cache using this key (per job)
+ primary-key: nix-${{ runner.os }}-${{ github.job }}
# if there's no cache hit, restore a cache by this prefix
restore-prefixes-first-match: nix-${{ runner.os }}
# collect garbage until the Nix store size (in bytes) is at most this number
# before trying to save a new cache
- # 1G = 1073741824
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
with:
diff --git a/.github/workflows/nix-update-inputs.yml b/.github/workflows/nix-update-inputs.yml
index c83e98801..a3084b270 100644
--- a/.github/workflows/nix-update-inputs.yml
+++ b/.github/workflows/nix-update-inputs.yml
@@ -27,25 +27,13 @@ jobs:
- name: Restore and save Nix store
uses: nix-community/cache-nix-action@v6
with:
- # restore and save a cache using this key
- primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
+ # restore and save a cache using this key (per job)
+ primary-key: nix-${{ runner.os }}-${{ github.job }}
# 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
# before trying to save a new cache
- # 1G = 1073741824
- 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
+ gc-max-store-size-linux: 5G
- name: Update inputs
run: nix/update-inputs.sh
diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml
index 1d514c507..b46b37953 100644
--- a/.github/workflows/nix.yml
+++ b/.github/workflows/nix.yml
@@ -25,25 +25,13 @@ jobs:
- name: Restore and save Nix store
uses: nix-community/cache-nix-action@v6
with:
- # restore and save a cache using this key
- primary-key: nix-${{ runner.os }}
+ # restore and save a cache using this key (per job)
+ primary-key: nix-${{ runner.os }}-${{ github.job }}
# if there's no cache hit, restore a cache by this prefix
restore-prefixes-first-match: nix-${{ runner.os }}
# collect garbage until the Nix store size (in bytes) is at most this number
# before trying to save a new cache
- # 1G = 1073741824
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
with:
diff --git a/.github/workflows/translation-ai-check.yml b/.github/workflows/translation-ai-check.yml
new file mode 100644
index 000000000..0729e9f61
--- /dev/null
+++ b/.github/workflows/translation-ai-check.yml
@@ -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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 29de4a324..7062e7d98 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
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)
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
@@ -128,13 +128,41 @@ set(HYPRGRAPHICS_VERSION "${hyprgraphics_dep_VERSION}")
find_package(Git QUIET)
-set(GIT_COMMIT_HASH "unknown")
-set(GIT_BRANCH "unknown")
-set(GIT_COMMIT_MESSAGE "unknown")
-set(GIT_COMMIT_DATE "unknown")
-set(GIT_DIRTY "unknown")
-set(GIT_TAG "unknown")
-set(GIT_COMMITS "0")
+# 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")
+endif()
+
+set(GIT_BRANCH "$ENV{GIT_BRANCH}")
+if(NOT GIT_BRANCH)
+ set(GIT_BRANCH "unknown")
+endif()
+
+set(GIT_COMMIT_MESSAGE "$ENV{GIT_COMMIT_MESSAGE}")
+if(NOT GIT_COMMIT_MESSAGE)
+ set(GIT_COMMIT_MESSAGE "unknown")
+endif()
+
+set(GIT_COMMIT_DATE "$ENV{GIT_COMMIT_DATE}")
+if(NOT GIT_COMMIT_DATE)
+ set(GIT_COMMIT_DATE "unknown")
+endif()
+
+set(GIT_DIRTY "$ENV{GIT_DIRTY}")
+if(NOT GIT_DIRTY)
+ set(GIT_DIRTY "unknown")
+endif()
+
+set(GIT_TAG "$ENV{GIT_TAG}")
+if(NOT GIT_TAG)
+ set(GIT_TAG "unknown")
+endif()
+
+set(GIT_COMMITS "$ENV{GIT_COMMITS}")
+if(NOT GIT_COMMITS)
+ set(GIT_COMMITS "0")
+endif()
if(Git_FOUND)
execute_process(
@@ -155,10 +183,10 @@ if(Git_FOUND)
execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current
WORKING_DIRECTORY ${GIT_TOPLEVEL}
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}
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}
OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --quiet HEAD --
@@ -192,7 +220,7 @@ pkg_check_modules(
deps
REQUIRED
IMPORTED_TARGET
- xkbcommon
+ xkbcommon>=1.11.0
uuid
wayland-server>=1.22.90
wayland-protocols>=1.45
@@ -205,7 +233,8 @@ pkg_check_modules(
libinput>=1.28
gbm
gio-2.0
- re2)
+ re2
+ muparser)
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
@@ -483,7 +512,6 @@ add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}")
# installable assets
file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*")
-list(FILTER INSTALLABLE_ASSETS EXCLUDE REGEX "meson.build")
install(FILES ${INSTALLABLE_ASSETS}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
diff --git a/assets/install/meson.build b/assets/install/meson.build
deleted file mode 100644
index 450764695..000000000
--- a/assets/install/meson.build
+++ /dev/null
@@ -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
diff --git a/assets/meson.build b/assets/meson.build
deleted file mode 100644
index 2a28121dd..000000000
--- a/assets/meson.build
+++ /dev/null
@@ -1,7 +0,0 @@
-install_data(
- 'hyprland-portals.conf',
- install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'),
- install_tag: 'runtime',
-)
-
-subdir('install')
diff --git a/docs/meson.build b/docs/meson.build
deleted file mode 100644
index 6ff51d1a5..000000000
--- a/docs/meson.build
+++ /dev/null
@@ -1,2 +0,0 @@
-install_man('Hyprland.1')
-install_man('hyprctl.1')
diff --git a/example/hyprland.conf b/example/hyprland.conf
index a1408dc38..07b372c10 100644
--- a/example/hyprland.conf
+++ b/example/hyprland.conf
@@ -159,10 +159,23 @@ animations {
# uncomment all if you wish to use that.
# workspace = w[tv1], gapsout:0, gapsin:0
# workspace = f[1], gapsout:0, gapsin:0
-# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
-# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
-# windowrule = bordersize 0, floating:0, onworkspace:f[1]
-# windowrule = rounding 0, floating:0, onworkspace:f[1]
+# windowrule {
+# name = no-gaps-wtv1
+# match:float = false
+# 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
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/Workspace-Rules/ for workspace rules
-# Example windowrule
-# windowrule = float,class:^(kitty)$,title:^(kitty)$
+# Example windowrules that are useful
-# Ignore maximize requests from apps. You'll probably like this.
-windowrule = suppressevent maximize, class:.*
+windowrule {
+ # Ignore maximize requests from all apps. You'll probably like this.
+ name = suppress-maximize-events
+ match:class = .*
-# Fix some dragging issues with XWayland
-windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
+ suppress_event = maximize
+}
+
+windowrule {
+ # Fix some dragging issues with XWayland
+ name = fix-xwayland-drags
+ match:class = ^$
+ match:title = ^$
+ match:xwayland = true
+ match:float = true
+ match:fullscreen = false
+ match:pin = false
+
+ no_focus = true
+}
diff --git a/example/meson.build b/example/meson.build
deleted file mode 100644
index a338644e0..000000000
--- a/example/meson.build
+++ /dev/null
@@ -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',
-)
diff --git a/flake.lock b/flake.lock
index 221c4558d..5df06468d 100644
--- a/flake.lock
+++ b/flake.lock
@@ -193,11 +193,11 @@
]
},
"locked": {
- "lastModified": 1758927902,
- "narHash": "sha256-LZgMds7M94+vuMql2bERQ6LiFFdhgsEFezE4Vn+Ys3A=",
+ "lastModified": 1763254292,
+ "narHash": "sha256-JNgz3Fz2KMzkT7aR72wsgu/xNeJB//LSmdilh8Z/Zao=",
"owner": "hyprwm",
"repo": "hyprlang",
- "rev": "4dafa28d4f79877d67a7d1a654cddccf8ebf15da",
+ "rev": "deea98d5b61d066bdc7a68163edd2c4bd28d3a6b",
"type": "github"
},
"original": {
@@ -261,11 +261,11 @@
]
},
"locked": {
- "lastModified": 1762387740,
- "narHash": "sha256-gQ9zJ+pUI4o+Gh4Z6jhJll7jjCSwi8ZqJIhCE2oqwhQ=",
+ "lastModified": 1763323331,
+ "narHash": "sha256-+Z0OfCo1MS8/aIutSAW5aJR9zTae1wz9kcJYMgpwN6M=",
"owner": "hyprwm",
"repo": "hyprutils",
- "rev": "926689ddb9c0a8787e58c02c765a62e32d63d1f7",
+ "rev": "0c6411851cc779d551edc89b83966696201611aa",
"type": "github"
},
"original": {
@@ -299,11 +299,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1762363567,
- "narHash": "sha256-YRqMDEtSMbitIMj+JLpheSz0pwEr0Rmy5mC7myl17xs=",
+ "lastModified": 1763283776,
+ "narHash": "sha256-Y7TDFPK4GlqrKrivOcsHG8xSGqQx3A6c+i7novT85Uk=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "ae814fd3904b621d8ab97418f1d0f2eb0d3716f4",
+ "rev": "50a96edd8d0db6cc8db57dab6bb6d6ee1f3dc49a",
"type": "github"
},
"original": {
@@ -322,11 +322,11 @@
]
},
"locked": {
- "lastModified": 1762441963,
- "narHash": "sha256-j+rNQ119ffYUkYt2YYS6rnd6Jh/crMZmbqpkGLXaEt0=",
+ "lastModified": 1763319842,
+ "narHash": "sha256-YG19IyrTdnVn0l3DvcUYm85u3PaqBt6tI6VvolcuHnA=",
"owner": "cachix",
"repo": "git-hooks.nix",
- "rev": "8e7576e79b88c16d7ee3bbd112c8d90070832885",
+ "rev": "7275fa67fbbb75891c16d9dee7d88e58aea2d761",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 5c58e26d0..6799144b4 100644
--- a/flake.nix
+++ b/flake.nix
@@ -151,7 +151,6 @@
(pkgsFor.${system})
# hyprland-packages
hyprland
- hyprland-with-hyprtester
hyprland-unwrapped
# hyprland-extras
xdg-desktop-portal-hyprland
diff --git a/hyprctl/meson.build b/hyprctl/meson.build
deleted file mode 100644
index d6769b84a..000000000
--- a/hyprctl/meson.build
+++ /dev/null
@@ -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',
-)
diff --git a/hyprpm/src/main.cpp b/hyprpm/src/main.cpp
index 47a557e2c..777d1d46e 100644
--- a/hyprpm/src/main.cpp
+++ b/hyprpm/src/main.cpp
@@ -25,8 +25,8 @@ constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
┃
┣ Flags:
┃
-┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events).
-┣ --notify-fail | -nn → Send a hyprland notification for fail events only.
+┣ --notify | -n → Send a hyprland notification confirming successful plugin load.
+┃ Warnings/Errors trigger notifications regardless of this flag.
┣ --help | -h → Show this menu.
┣ --verbose | -v → Enable too much logging.
┣ --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 command;
- bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
+ bool notify = false, verbose = false, force = false, noShallow = false;
std::string customHlUrl;
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") {
notify = true;
} 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") {
verbose = true;
} 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)
return 1;
- } else if (notify)
+ } else {
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
+ }
} else if (command[0] == "enable") {
if (command.size() < 2) {
std::println(stderr, "{}", failureString("Not enough args for enable."));
@@ -194,19 +197,17 @@ int main(int argc, char** argv, char** envp) {
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
if (ret != LOADSTATE_OK) {
- if (notify) {
- switch (ret) {
- case LOADSTATE_FAIL:
- case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
- case LOADSTATE_HEADERS_OUTDATED:
- g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
- break;
- default: break;
- }
+ switch (ret) {
+ case LOADSTATE_FAIL:
+ case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
+ case LOADSTATE_HEADERS_OUTDATED:
+ g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
+ break;
+ default: break;
}
return 1;
- } else if (notify && !notifyFail) {
+ } else if (notify) {
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
}
} else if (command[0] == "purge-cache") {
diff --git a/hyprpm/src/meson.build b/hyprpm/src/meson.build
deleted file mode 100644
index fd914f9d2..000000000
--- a/hyprpm/src/meson.build
+++ /dev/null
@@ -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',
-)
diff --git a/hyprtester/plugin/src/main.cpp b/hyprtester/plugin/src/main.cpp
index 1d0b68dcc..72120eaca 100644
--- a/hyprtester/plugin/src/main.cpp
+++ b/hyprtester/plugin/src/main.cpp
@@ -11,6 +11,8 @@
#include
#include
#include
+#include
+#include
#include
#undef private
@@ -245,6 +247,30 @@ static SDispatchResult keybind(std::string in) {
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) {
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:scroll", ::scroll);
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
g_mouse = CTestMouse::create(false);
diff --git a/hyprtester/src/tests/clients/pointer-scroll.cpp b/hyprtester/src/tests/clients/pointer-scroll.cpp
index e1ba237fa..2ea93a14c 100644
--- a/hyprtester/src/tests/clients/pointer-scroll.cpp
+++ b/hyprtester/src/tests/clients/pointer-scroll.cpp
@@ -64,7 +64,7 @@ static bool startClient(SClient& client) {
// wait for window to appear
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);
return false;
}
@@ -130,7 +130,7 @@ static bool test() {
EXPECT(sendScroll(10), true);
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(getLastDelta(client), 40);
diff --git a/hyprtester/src/tests/clients/pointer-warp.cpp b/hyprtester/src/tests/clients/pointer-warp.cpp
index f37b94c35..bb03afd28 100644
--- a/hyprtester/src/tests/clients/pointer-warp.cpp
+++ b/hyprtester/src/tests/clients/pointer-warp.cpp
@@ -64,7 +64,7 @@ static bool startClient(SClient& client) {
// wait for window to appear
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);
return false;
}
diff --git a/hyprtester/src/tests/main/dwindle.cpp b/hyprtester/src/tests/main/dwindle.cpp
index a75646c8d..8f17c8152 100644
--- a/hyprtester/src/tests/main/dwindle.cpp
+++ b/hyprtester/src/tests/main/dwindle.cpp
@@ -16,6 +16,7 @@ static void testFloatClamp() {
}
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 setfloating class:c"));
OK(getFromSocket("/dispatch resizewindowpixel exact 1200 900,class:c"));
@@ -24,7 +25,7 @@ static void testFloatClamp() {
{
auto str = getFromSocket("/clients");
- EXPECT_CONTAINS(str, "at: 718,178");
+ EXPECT_CONTAINS(str, "at: 698,158");
EXPECT_CONTAINS(str, "size: 1200,900");
}
diff --git a/hyprtester/src/tests/main/hyprctl.cpp b/hyprtester/src/tests/main/hyprctl.cpp
index 95f7caaef..e8759d28c 100644
--- a/hyprtester/src/tests/main/hyprctl.cpp
+++ b/hyprtester/src/tests/main/hyprctl.cpp
@@ -56,82 +56,82 @@ static bool testGetprop() {
return false;
}
- // animationstyle
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "(unset)");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": ""})");
- getFromSocket("/dispatch setprop class:kitty animationstyle teststyle");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "teststyle");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": "teststyle"})");
+ // animation
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "(unset)");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": ""})");
+ getFromSocket("/dispatch setprop class:kitty animation teststyle");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "teststyle");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": "teststyle"})");
- // maxsize
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "inf inf");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [null,null]})");
- getFromSocket("/dispatch setprop class:kitty maxsize 200 150");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "200 150");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [200,150]})");
+ // max_size
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "inf inf");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [null,null]})");
+ getFromSocket("/dispatch setprop class:kitty max_size 200 150");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "200 150");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [200,150]})");
- // minsize
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "20 20");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [20,20]})");
- getFromSocket("/dispatch setprop class:kitty minsize 100 50");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "100 50");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [100,50]})");
+ // min_size
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "20 20");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [20,20]})");
+ getFromSocket("/dispatch setprop class:kitty min_size 100 50");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "100 50");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [100,50]})");
- // alpha
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "1");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 1})");
- getFromSocket("/dispatch setprop class:kitty alpha 0.3");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "0.3");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 0.3})");
+ // opacity
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "1");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 1})");
+ getFromSocket("/dispatch setprop class:kitty opacity 0.3");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "0.3");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 0.3})");
- // alphainactive
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "1");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 1})");
- getFromSocket("/dispatch setprop class:kitty alphainactive 0.5");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "0.5");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 0.5})");
+ // opacity_inactive
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "1");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 1})");
+ getFromSocket("/dispatch setprop class:kitty opacity_inactive 0.5");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "0.5");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 0.5})");
- // alphafullscreen
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "1");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 1})");
- getFromSocket("/dispatch setprop class:kitty alphafullscreen 0.75");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "0.75");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 0.75})");
+ // opacity_fullscreen
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "1");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 1})");
+ getFromSocket("/dispatch setprop class:kitty opacity_fullscreen 0.75");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "0.75");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 0.75})");
- // alphaoverride
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": false})");
- getFromSocket("/dispatch setprop class:kitty alphaoverride true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": true})");
+ // opacity_override
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": false})");
+ getFromSocket("/dispatch setprop class:kitty opacity_override true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": true})");
- // alphainactiveoverride
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": false})");
- getFromSocket("/dispatch setprop class:kitty alphainactiveoverride true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": true})");
+ // opacity_inactive_override
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": false})");
+ getFromSocket("/dispatch setprop class:kitty opacity_inactive_override true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": true})");
- // alphafullscreenoverride
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": false})");
- getFromSocket("/dispatch setprop class:kitty alphafullscreenoverride true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": true})");
+ // opacity_fullscreen_override
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": false})");
+ getFromSocket("/dispatch setprop class:kitty opacity_fullscreen_override true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": true})");
- // activebordercolor
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ee33ccff ee00ff99 45deg");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ee33ccff ee00ff99 45deg"})");
- getFromSocket("/dispatch setprop class:kitty activebordercolor rgb(abcdef)");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ffabcdef 0deg");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ffabcdef 0deg"})");
+ // active_border_color
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "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 active_border_color rgb(abcdef)");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ffabcdef 0deg");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ffabcdef 0deg"})");
// bool window properties
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "false");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": false})");
- getFromSocket("/dispatch setprop class:kitty allowsinput true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "true");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": true})");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "false");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": false})");
+ getFromSocket("/dispatch setprop class:kitty allows_input true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "true");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": true})");
// int window properties
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})");
// float window properties
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "2");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 2})");
- getFromSocket("/dispatch setprop class:kitty roundingpower 1.25");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "1.25");
- EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 1.25})");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "2");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 2})");
+ getFromSocket("/dispatch setprop class:kitty rounding_power 1.25");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "1.25");
+ EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 1.25})");
// errors
EXPECT(getCommandStdOut("hyprctl getprop"), "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");
// kill all
diff --git a/hyprtester/src/tests/main/tags.cpp b/hyprtester/src/tests/main/tags.cpp
index 22bedcde3..c345fe718 100644
--- a/hyprtester/src/tests/main/tags.cpp
+++ b/hyprtester/src/tests/main/tags.cpp
@@ -21,21 +21,24 @@ static bool testTags() {
NLog::log("{}Testing testTag tags", Colors::YELLOW);
- OK(getFromSocket("/keyword windowrule tag +testTag, class:tagged"));
- OK(getFromSocket("/keyword windowrule noshadow, tag:negative:testTag"));
- OK(getFromSocket("/keyword windowrule noborder, tag:testTag"));
+ OK(getFromSocket("/keyword windowrule[tag-test-1]:tag +testTag"));
+ OK(getFromSocket("/keyword windowrule[tag-test-1]:match:class tagged"));
+ 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);
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("/getprop activewindow noborder"), "true");
- EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "false");
- NLog::log("{}Testing untagged window for noborder & noshadow", Colors::YELLOW);
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "true");
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "false");
+ NLog::log("{}Testing untagged window for no_dim & no_shadow", Colors::YELLOW);
OK(getFromSocket("/dispatch focuswindow class:untagged"));
EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag");
- EXPECT_CONTAINS(getFromSocket("/getprop activewindow noborder"), "false");
- EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "true");
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "true");
+ EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "false");
Tests::killAllWindows();
EXPECT(Tests::windowCount(), 0);
diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp
index 6cfa061c5..871875927 100644
--- a/hyprtester/src/tests/main/window.cpp
+++ b/hyprtester/src/tests/main/window.cpp
@@ -152,40 +152,22 @@ static bool test() {
NLog::log("{}Testing window split ratios", Colors::YELLOW);
{
- const double INITIAL_RATIO = 1.25;
- const int GAPSIN = 5;
- const int GAPSOUT = 20;
- const int BORDERSIZE = 2;
- const int BORDERS = BORDERSIZE * 2;
- const int MONITOR_W = 1920;
- const int MONITOR_H = 1080;
-
- 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);
+ const double RATIO = 1.25;
+ const double PERCENT = RATIO / 2.0 * 100.0;
+ const int GAPSIN = 5;
+ const int GAPSOUT = 20;
+ const int BORDERS = 2 * 2;
+ const int WTRIM = BORDERS + GAPSIN + GAPSOUT;
+ const int HEIGHT = 1080 - (BORDERS + (GAPSOUT * 2));
+ const int WIDTH1 = std::round(1920.0 / 2.0 * (2 - RATIO)) - WTRIM;
+ const int WIDTH2 = std::round(1920.0 / 2.0 * RATIO) - WTRIM;
OK(getFromSocket("/keyword dwindle:default_split_ratio 1.25"));
if (!spawnKitty("kitty_B"))
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));
OK(getFromSocket("/dispatch killwindow activewindow"));
@@ -197,12 +179,12 @@ static bool test() {
if (!spawnKitty("kitty_B"))
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));
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
- NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT);
- EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", 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: {},{}", WIDTH1, HEIGHT));
OK(getFromSocket("/keyword dwindle:default_split_ratio 1"));
}
@@ -246,12 +228,15 @@ static bool test() {
testSwapWindow();
+ getFromSocket("/dispatch workspace 1");
+
NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW);
{
// Enable the config for testing, test max/minsize for tiled windows and centering
OK(getFromSocket("/keyword misc:size_limits_tiled 1"));
- OK(getFromSocket("/keyword windowrule maxsize 1500 500, class:kitty_maxsize"));
- OK(getFromSocket("/keyword windowrule minsize 1200 500, class:kitty_maxsize"));
+ OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match: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"))
return false;
@@ -297,29 +282,127 @@ static bool test() {
EXPECT_CONTAINS(str, "floating: 1");
EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE));
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 workspace special:magic, class:magic_kitty"));
+ }
- if (!spawnKitty("magic_kitty"))
- return false;
- EXPECT_CONTAINS(getFromSocket("/activewindow"), "special:magic");
+ OK(getFromSocket("/keyword windowrule[wr-kitty-stuff]:opacity 0.5 0.5 override"));
+
+ {
+ 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"))
+ return false;
+
+ {
+ auto str = getFromSocket("/activewindow");
+ EXPECT_CONTAINS(str, "special:magic");
EXPECT_NOT_CONTAINS(str, "workspace: 9");
}
- NLog::log("{}Testing faulty rules", Colors::YELLOW);
- {
- 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)
+ if (auto str = getFromSocket("/monitors"); str.contains("magic)")) {
+ OK(getFromSocket("/dispatch togglespecialworkspace magic"));
}
+ 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);
OK(getFromSocket("/reload"));
diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp
index def35d088..622236dcf 100644
--- a/hyprtester/src/tests/main/workspaces.cpp
+++ b/hyprtester/src/tests/main/workspaces.cpp
@@ -6,7 +6,6 @@
#include
#include
#include
-#include
#include
#include
#include "../shared.hpp"
@@ -15,7 +14,6 @@ static int ret = 0;
using namespace Hyprutils::OS;
using namespace Hyprutils::Memory;
-using namespace Hyprutils::Utils;
#define UP CUniquePointer
#define SP CSharedPointer
@@ -27,7 +25,7 @@ static bool test() {
// test on workspace "window"
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);
OK(getFromSocket("r/keyword workspace 966,persistent:1"));
@@ -361,95 +359,6 @@ static bool test() {
NLog::log("{}Killing all windows", Colors::YELLOW);
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);
EXPECT(Tests::windowCount(), 0);
diff --git a/hyprtester/test.conf b/hyprtester/test.conf
index 047001c26..ac28bc5a6 100644
--- a/hyprtester/test.conf
+++ b/hyprtester/test.conf
@@ -318,28 +318,70 @@ submap = reset
### WINDOWS AND WORKSPACES ###
##############################
-# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
-# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
+windowrule {
+ # Ignore maximize requests from apps. You'll probably like this.
+ name = suppress-maximize-events
+ match:class = .*
-# Example windowrule v1
-# windowrule = float, ^(kitty)$
+ suppress_event = maximize
+}
-# Example windowrule v2
-# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
+windowrule {
+ # Fix some dragging issues with XWayland
+ name = fix-xwayland-drags
+ match:class = ^$
+ match:title = ^$
+ match:xwayland = true
+ match:float = true
+ match:fullscreen = false
+ match:pin = false
-# Ignore maximize requests from apps. You'll probably like this.
-windowrulev2 = suppressevent maximize, class:.*
+ no_focus = true
+}
-# Fix some dragging issues with XWayland
-windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
-
-# Workspace "windows" is a smart gaps one
workspace = n[s:window] w[tv1], 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]
-windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] f[1]
-windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] f[1]
+
+windowrule {
+ name = smart-gaps-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, right, float
@@ -356,7 +398,3 @@ gesture = 5, left, dispatcher, sendshortcut, , i, activewindow
gesture = 5, right, dispatcher, sendshortcut, , t, activewindow
gesture = 4, right, dispatcher, sendshortcut, , return, activewindow
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
diff --git a/meson.build b/meson.build
deleted file mode 100644
index c7819e80b..000000000
--- a/meson.build
+++ /dev/null
@@ -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'],
-)
diff --git a/meson_options.txt b/meson_options.txt
deleted file mode 100644
index e50b4ccee..000000000
--- a/meson_options.txt
+++ /dev/null
@@ -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')
diff --git a/nix/default.nix b/nix/default.nix
index 0fe57191a..45fd273b8 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -26,6 +26,7 @@
libxkbcommon,
libuuid,
libgbm,
+ muparser,
pango,
pciutils,
re2,
@@ -45,12 +46,12 @@
commit,
revCount,
date,
- withHyprtester ? false,
# deprecated flags
enableNvidiaPatches ? false,
nvidiaPatches ? false,
hidpiXWayland ? false,
legacyRenderer ? false,
+ withHyprtester ? false,
}: let
inherit (builtins) foldl' readFile;
inherit (lib.asserts) assertMsg;
@@ -70,6 +71,7 @@ in
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 (!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: {
pname = "hyprland${optionalString debug "-debug"}";
inherit version;
@@ -85,6 +87,7 @@ in
../assets/install
../hyprctl
../hyprland.pc.in
+ ../hyprtester
../LICENSE
../protocols
../src
@@ -94,7 +97,6 @@ in
(fs.fileFilter (file: file.hasExt "conf" || file.hasExt "desktop") ../example)
(fs.fileFilter (file: file.hasExt "sh") ../scripts)
(fs.fileFilter (file: file.name == "CMakeLists.txt") ../.)
- (optional withHyprtester ../hyprtester)
]));
};
@@ -106,11 +108,13 @@ in
sed -i "s#@PREFIX@/##g" hyprland.pc.in
'';
- COMMITS = revCount;
- DATE = date;
- DIRTY = optionalString (commit == "") "dirty";
- HASH = commit;
- TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
+ env = {
+ GIT_COMMITS = revCount;
+ GIT_COMMIT_DATE = date;
+ GIT_COMMIT_HASH = commit;
+ GIT_DIRTY = if (commit == "") then "clean" else "dirty";
+ GIT_TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
+ };
depsBuildBuild = [
pkg-config
@@ -146,6 +150,7 @@ in
libuuid
libxkbcommon
libgbm
+ muparser
pango
pciutils
re2
@@ -187,7 +192,7 @@ in
"NO_UWSM" = true;
"NO_HYPRPM" = true;
"TRACY_ENABLE" = false;
- "BUILD_HYPRTESTER" = withHyprtester;
+ "BUILD_HYPRTESTER" = true;
};
preConfigure = ''
@@ -206,7 +211,7 @@ in
pkgconf
]}
''}
- '' + optionalString withHyprtester ''
+
install hyprtester/pointer-warp -t $out/bin
install hyprtester/pointer-scroll -t $out/bin
'';
diff --git a/nix/overlays.nix b/nix/overlays.nix
index 7f6bf2ae3..c7ef95b86 100644
--- a/nix/overlays.nix
+++ b/nix/overlays.nix
@@ -43,7 +43,12 @@ in {
};
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
hyprland-legacy-renderer =
diff --git a/nix/tests/default.nix b/nix/tests/default.nix
index d7c000617..ef92a4635 100644
--- a/nix/tests/default.nix
+++ b/nix/tests/default.nix
@@ -1,6 +1,6 @@
inputs: pkgs: let
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
- hyprland = flake.hyprland-with-hyprtester;
+ hyprland = flake.hyprland;
in {
tests = pkgs.testers.runNixOSTest {
name = "hyprland-tests";
diff --git a/protocols/meson.build b/protocols/meson.build
deleted file mode 100644
index 33663fa38..000000000
--- a/protocols/meson.build
+++ /dev/null
@@ -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,
-)
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 2dc798e47..0f24a8bfc 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -65,6 +65,7 @@
#include "debug/HyprNotificationOverlay.hpp"
#include "debug/HyprDebugOverlay.hpp"
#include "helpers/MonitorFrameScheduler.hpp"
+#include "i18n/Engine.hpp"
#include
#include
@@ -900,8 +901,8 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
if (ONLY_PRIORITY && !w->priorityFocus())
continue;
- if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_windowData.noFocus.valueOrDefault() && w != pIgnoreWindow &&
- !isShadowedByModal(w)) {
+ if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
+ w != pIgnoreWindow && !isShadowedByModal(w)) {
const auto BB = w->getWindowBoxUnified(properties);
CBox box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0);
if (box.containsPoint(g_pPointerManager->position()))
@@ -938,7 +939,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
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)) {
// OR windows should add focus to parent
if (w->m_X11ShouldntFocus && !w->isX11OverrideRedirect())
@@ -999,7 +1000,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
continue;
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))
return w;
}
@@ -1015,7 +1016,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper
if (!w->m_workspace)
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)) {
CBox box = (properties & USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_position, w->m_size};
if (box.containsPoint(pos))
@@ -1151,7 +1152,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP pSurface
m_lastWindow.reset();
if (PLASTWINDOW && PLASTWINDOW->m_isMapped) {
- updateWindowAnimatedDecorationValues(PLASTWINDOW);
+ PLASTWINDOW->updateDecorationValues();
g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
}
@@ -1171,7 +1172,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP pSurface
return;
}
- if (pWindow->m_windowData.noFocus.valueOrDefault()) {
+ if (pWindow->m_ruleApplicator->noFocus().valueOrDefault()) {
Debug::log(LOG, "Ignoring focus to nofocus window!");
return;
}
@@ -1208,9 +1209,9 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP pSurface
// 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) {
- PLASTWINDOW->updateDynamicRules();
+ PLASTWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FOCUS);
- updateWindowAnimatedDecorationValues(PLASTWINDOW);
+ PLASTWINDOW->updateDecorationValues();
if (!pWindow->m_isX11 || !pWindow->isX11OverrideRedirect())
g_pXWaylandManager->activateWindow(PLASTWINDOW, false);
@@ -1224,10 +1225,10 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, SP pSurface
g_pXWaylandManager->activateWindow(pWindow, true); // sets the m_pLastWindow
- pWindow->updateDynamicRules();
+ pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FOCUS);
pWindow->onFocusAnimUpdate();
- updateWindowAnimatedDecorationValues(pWindow);
+ pWindow->updateDecorationValues();
if (pWindow->m_isUrgent)
pWindow->m_isUrgent = false;
@@ -1333,7 +1334,7 @@ SP CCompositor::vectorToLayerSurface(const Vector2D& pos, st
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 ||
- (aboveLockscreen && (!ls->m_aboveLockscreen || !ls->m_aboveLockscreenInteractable)))
+ (aboveLockscreen && ls->m_ruleApplicator->aboveLock().valueOrDefault() != 2))
continue;
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 floating) {
template
static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional floating, bool anyWorkspace = false) {
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
@@ -1905,103 +1906,10 @@ void CCompositor::updateAllWindowsAnimatedDecorationValues() {
if (!w->m_isMapped)
continue;
- updateWindowAnimatedDecorationValues(w);
+ w->updateDecorationValues();
}
}
-void CCompositor::updateWindowAnimatedDecorationValues(PHLWINDOW pWindow) {
- // optimization
- static auto PACTIVECOL = CConfigValue("general:col.active_border");
- static auto PINACTIVECOL = CConfigValue("general:col.inactive_border");
- static auto PNOGROUPACTIVECOL = CConfigValue("general:col.nogroup_border_active");
- static auto PNOGROUPINACTIVECOL = CConfigValue("general:col.nogroup_border");
- static auto PGROUPACTIVECOL = CConfigValue("group:col.border_active");
- static auto PGROUPINACTIVECOL = CConfigValue("group:col.border_inactive");
- static auto PGROUPACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_active");
- static auto PGROUPINACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_inactive");
- static auto PINACTIVEALPHA = CConfigValue("decoration:inactive_opacity");
- static auto PACTIVEALPHA = CConfigValue("decoration:active_opacity");
- static auto PFULLSCREENALPHA = CConfigValue("decoration:fullscreen_opacity");
- static auto PSHADOWCOL = CConfigValue("decoration:shadow:color");
- static auto PSHADOWCOLINACTIVE = CConfigValue("decoration:shadow:color_inactive");
- static auto PDIMSTRENGTH = CConfigValue("decoration:dim_strength");
- static auto PDIMENABLED = CConfigValue("decoration:dim_inactive");
- static auto PDIMMODAL = CConfigValue("decoration:dim_modal");
-
- auto* const ACTIVECOL = sc((PACTIVECOL.ptr())->getData());
- auto* const INACTIVECOL = sc((PINACTIVECOL.ptr())->getData());
- auto* const NOGROUPACTIVECOL = sc((PNOGROUPACTIVECOL.ptr())->getData());
- auto* const NOGROUPINACTIVECOL = sc((PNOGROUPINACTIVECOL.ptr())->getData());
- auto* const GROUPACTIVECOL = sc((PGROUPACTIVECOL.ptr())->getData());
- auto* const GROUPINACTIVECOL = sc((PGROUPINACTIVECOL.ptr())->getData());
- auto* const GROUPACTIVELOCKEDCOL = sc((PGROUPACTIVELOCKEDCOL.ptr())->getData());
- auto* const GROUPINACTIVELOCKEDCOL = sc((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) {
// 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]; }))
@@ -2340,14 +2248,14 @@ void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, cons
}
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});
else
setWindowFullscreenState(PWINDOW, SFullscreenState{.internal = MODE, .client = PWINDOW->m_fullscreenState.client});
}
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});
else
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
- if (!CHANGEINTERNAL && PWINDOW->m_windowData.syncFullscreen.valueOrDefault())
+ if (!CHANGEINTERNAL && PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
return;
PWINDOW->m_fullscreenState.client = state.client;
g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN);
if (!CHANGEINTERNAL) {
- PWINDOW->updateDynamicRules();
- updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
+ Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
+ PWINDOW->updateDecorationValues();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
return;
}
@@ -2410,8 +2319,10 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, SFullscreenS
g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc(EFFECTIVE_MODE) != FSMODE_NONE)});
EMIT_HOOK_EVENT("fullscreen", PWINDOW);
- PWINDOW->updateDynamicRules();
- updateWindowAnimatedDecorationValues(PWINDOW);
+ PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
+ Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
+
+ PWINDOW->updateDecorationValues();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
// 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: {
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)) {
tagMatched = true;
break;
@@ -2765,22 +2676,19 @@ void CCompositor::performUserChecks() {
const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP");
if (!CURRENT_DESKTOP_ENV || std::string{CURRENT_DESKTOP_ENV} != "Hyprland") {
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.",
- CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"),
- CHyprColor{}, 15000, ICON_WARNING);
+ I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, {{"value", CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"}}), CHyprColor{}, 15000,
+ ICON_WARNING);
}
}
if (!*PNOCHECKGUIUTILS) {
if (!NFsUtils::executableExistsInPath("hyprland-dialog")) {
- g_pHyprNotificationOverlay->addNotification(
- "Your system does not have hyprland-guiutils installed. This is a runtime dependency for some dialogs. Consider installing it.", CHyprColor{}, 15000, ICON_WARNING);
+ g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_GUIUTILS), CHyprColor{}, 15000, ICON_WARNING);
}
}
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_pHyprOpenGL->m_failedAssetsNo, g_pHyprOpenGL->m_failedAssetsNo > 1 ? "s" : ""),
+ g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_ASSETS, {{"count", std::to_string(g_pHyprOpenGL->m_failedAssetsNo)}}),
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->updateDynamicRules();
+ pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
pWindow->uncacheWindowDecos();
pWindow->updateGroupOutputs();
@@ -2876,7 +2784,7 @@ PHLWINDOW CCompositor::getForceFocus() {
if (!w->m_isMapped || w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible())
continue;
- if (!w->m_stayFocused)
+ if (!w->m_ruleApplicator->stayFocused().valueOrDefault())
continue;
return w;
@@ -2905,10 +2813,8 @@ void CCompositor::checkMonitorOverlaps() {
for (const auto& m : m_monitors) {
if (!monitorRegion.copy().intersect(m->logicalBox()).empty()) {
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 "
- "layout.\nPlease see the wiki (Monitors page) for more. This will cause issues.",
- m->m_name),
- CHyprColor{}, 15000, ICON_WARNING);
+ g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, {{"name", m->m_name}}), CHyprColor{}, 15000,
+ ICON_WARNING);
break;
}
@@ -3029,6 +2935,8 @@ void CCompositor::arrangeMonitors() {
#ifndef NO_XWAYLAND
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);
#endif
}
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index bf6401e51..30b0f1bd0 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -129,7 +129,6 @@ class CCompositor {
PHLMONITOR getMonitorInDirection(const char&);
PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&);
void updateAllWindowsAnimatedDecorationValues();
- void updateWindowAnimatedDecorationValues(PHLWINDOW);
MONITORID getNextAvailableMonitorID(std::string const& name);
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR);
diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp
index 926d49ac7..f819e2930 100644
--- a/src/config/ConfigDescriptions.hpp
+++ b/src/config/ConfigDescriptions.hpp
@@ -15,12 +15,6 @@ inline static const std::vector CONFIG_OPTIONS = {
.type = CONFIG_OPTION_INT,
.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{
.value = "general:gaps_in",
.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 CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.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:
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index a3c60d2bb..775d3beb0 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -8,12 +8,15 @@
#include "../render/decorations/CHyprGroupBarDecoration.hpp"
#include "config/ConfigDataValues.hpp"
#include "config/ConfigValue.hpp"
-#include "../desktop/WindowRule.hpp"
#include "../protocols/LayerShell.hpp"
#include "../xwayland/XWayland.hpp"
#include "../protocols/OutputManagement.hpp"
#include "../managers/animation/AnimationManager.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 "../render/Renderer.hpp"
@@ -299,54 +302,6 @@ static Hyprlang::CParseResult handleUnbind(const char* c, const char* v) {
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) {
const std::string VALUE = v;
const std::string COMMAND = c;
@@ -431,6 +386,30 @@ static Hyprlang::CParseResult handleGesture(const char* c, const char* v) {
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) {
m_configValueNumber++;
m_config->addConfigValue(name, val);
@@ -463,7 +442,6 @@ CConfigManager::CConfigManager() {
m_config = makeUnique(m_configPaths.begin()->c_str(), Hyprlang::SConfigOptions{.throwAllErrors = true, .allowMissingConfig = true});
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_out", Hyprlang::CConfigCustomValueType{configHandleGapSet, configHandleGapDestroy, "20"});
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:keep_upper_gap", Hyprlang::INT{1});
registerConfigVar("group:groupbar:text_offset", Hyprlang::INT{0});
+ registerConfigVar("group:groupbar:blur", Hyprlang::INT{0});
registerConfigVar("debug:log_damage", 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_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
m_config->registerHandler(&::handleExec, "exec", {false});
m_config->registerHandler(&::handleRawExec, "execr", {false});
@@ -867,14 +856,12 @@ CConfigManager::CConfigManager() {
m_config->registerHandler(&::handleBind, "bind", {true});
m_config->registerHandler(&::handleUnbind, "unbind", {false});
m_config->registerHandler(&::handleWorkspaceRules, "workspace", {false});
- m_config->registerHandler(&::handleWindowRule, "windowrule", {false});
- m_config->registerHandler(&::handleLayerRule, "layerrule", {false});
- m_config->registerHandler(&::handleWindowRuleV2, "windowrulev2", {false});
+ m_config->registerHandler(&::handleWindowrule, "windowrule", {false});
+ m_config->registerHandler(&::handleLayerrule, "layerrule", {false});
m_config->registerHandler(&::handleBezier, "bezier", {false});
m_config->registerHandler(&::handleAnimation, "animation", {false});
m_config->registerHandler(&::handleSource, "source", {false});
m_config->registerHandler(&::handleSubmap, "submap", {false});
- m_config->registerHandler(&::handleBlurLS, "blurls", {false});
m_config->registerHandler(&::handlePlugin, "plugin", {false});
m_config->registerHandler(&::handlePermission, "permission", {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}); });
}
+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 CConfigManager::generateConfig(std::string configPath) {
std::string parentPath = std::filesystem::path(configPath).parent_path();
@@ -983,13 +990,36 @@ std::string CConfigManager::getErrors() {
return m_configErrors;
}
+static std::vector 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() {
EMIT_HOOK_EVENT("preConfigReload", nullptr);
setDefaultAnimationVars();
resetHLConfig();
- m_configCurrentPath = getMainConfigPath();
- const auto ERR = m_config->parse();
+ m_configCurrentPath = getMainConfigPath();
+
+ exportHlVersionVars();
+
+ const auto ERR = m_config->parse();
+
+ clearHlVersionVars();
+
const auto monitorError = handleMonitorv2();
+ const auto ruleError = reloadRules();
m_lastConfigVerificationWasSuccessful = !ERR.error && !monitorError.error;
postConfigReload(ERR.error || !monitorError.error ? ERR : monitorError);
}
@@ -1057,20 +1087,18 @@ void CConfigManager::setDefaultAnimationVars() {
std::optional CConfigManager::resetHLConfig() {
m_monitorRules.clear();
- m_windowRules.clear();
g_pKeybindManager->clearKeybinds();
g_pAnimationManager->removeAllBeziers();
g_pAnimationManager->addBezierWithName("linear", Vector2D(0.0, 0.0), Vector2D(1.0, 1.0));
g_pTrackpadGestures->clearGestures();
m_mAdditionalReservedAreas.clear();
- m_blurLSNamespaces.clear();
m_workspaceRules.clear();
setDefaultAnimationVars(); // reset anims
m_declaredPlugins.clear();
- m_layerRules.clear();
m_failedPluginConfigValues.clear();
m_finalExecRequests.clear();
+ m_keywordRules.clear();
// paths
m_configPaths.clear();
@@ -1080,6 +1108,8 @@ std::optional CConfigManager::resetHLConfig() {
const auto RET = verifyConfigExists();
+ reloadRuleConfigs();
+
return RET;
}
@@ -1178,6 +1208,77 @@ Hyprlang::CParseResult CConfigManager::handleMonitorv2() {
return result;
}
+std::optional 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(ENABLED->getValue()) == 0)
+ return std::nullopt;
+
+ SP rule = makeShared(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(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(VAL->getValue()));
+ }
+
+ Desktop::Rule::ruleEngine()->registerRule(std::move(rule));
+ return std::nullopt;
+}
+
+std::optional 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(ENABLED->getValue()) != 0)
+ return std::nullopt;
+
+ SP rule = makeShared(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(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(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{rule});
+ }
+
+ Desktop::Rule::ruleEngine()->updateAllRules();
+
+ return result;
+}
+
void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
updateWatcher();
@@ -1503,229 +1604,6 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1,
return mergedRule;
}
-std::vector> CConfigManager::getMatchingRules(PHLWINDOW pWindow, bool dynamic, bool shadowExec) {
- if (!valid(pWindow))
- return std::vector>();
-
- // if the window is unmapped, don't process exec rules yet.
- shadowExec = shadowExec || !pWindow->m_isMapped;
-
- std::vector> 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 internalMode, clientMode;
-
- if (ARGS[0] == "*")
- internalMode = std::nullopt;
- else if (isNumber(ARGS[0]))
- internalMode = sc(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(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 PIDs = {sc(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(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> CConfigManager::getMatchingRules(PHLLS pLS) {
- std::vector> 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(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(pLS->m_layerSurface->m_layerNamespace, "blur"));
-
- return returns;
-}
-
void CConfigManager::dispatchExecOnce() {
if (m_firstExecDispatched || m_isFirstLaunch)
return;
@@ -1831,16 +1709,6 @@ bool CConfigManager::deviceConfigExists(const std::string& dev) {
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() {
for (auto const& rm : g_pCompositor->m_realMonitors) {
if (!rm->m_output || rm->m_isUnsafeFallback)
@@ -1894,7 +1762,7 @@ void CConfigManager::ensureVRR(PHLMONITOR pMonitor) {
return; // ???
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;
if (wantVRR && USEVRR == 3) {
@@ -1963,10 +1831,6 @@ const std::vector& CConfigManager::getAllWorkspaceRules() {
return m_workspaceRules;
}
-void CConfigManager::addExecRule(const SExecRequestedRule& rule) {
- m_execRequestedRules.push_back(rule);
-}
-
void CConfigManager::handlePluginLoads() {
if (!g_pPluginSystem)
return;
@@ -2675,239 +2539,6 @@ std::optional CConfigManager::handleUnbind(const std::string& comma
return {};
}
-std::optional CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
- const auto VARLIST = CVarList(value, 0, ',', true);
-
- std::vector tokens;
- std::unordered_map 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 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> 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(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(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 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(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(ls.get())) == matchName)
- ls->m_forceBlur = forceBlur;
- } else if (ls->m_namespace == matchName)
- ls->m_forceBlur = forceBlur;
- }
- }
- }
-}
-
-std::optional 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 CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) {
// This can either be the monitor or the workspace identifier
const auto FIRST_DELIM = value.find_first_of(',');
@@ -3228,6 +2859,82 @@ std::optional CConfigManager::handleGesture(const std::string& comm
return std::nullopt;
}
+std::optional CConfigManager::handleWindowrule(const std::string& command, const std::string& value) {
+ CVarList2 data(std::string{value}, 0, ',');
+
+ SP rule = makeShared();
+
+ 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{m_keywordRules.back()});
+
+ return std::nullopt;
+}
+
+std::optional CConfigManager::handleLayerrule(const std::string& command, const std::string& value) {
+ CVarList2 data(std::string{value}, 0, ',');
+
+ SP rule = makeShared();
+
+ 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& CConfigManager::getAllDescriptions() {
return CONFIG_OPTIONS;
}
diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp
index 7f32be419..599ee8e71 100644
--- a/src/config/ConfigManager.hpp
+++ b/src/config/ConfigManager.hpp
@@ -13,14 +13,12 @@
#include
#include "../helpers/Monitor.hpp"
#include "../desktop/Window.hpp"
-#include "../desktop/LayerRule.hpp"
#include "ConfigDataValues.hpp"
#include "../SharedDefs.hpp"
#include "../helpers/Color.hpp"
#include "../desktop/DesktopTypes.hpp"
#include "../helpers/memory/Memory.hpp"
-#include "../desktop/WindowRule.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../managers/KeybindManager.hpp"
@@ -68,11 +66,6 @@ struct SPluginVariable {
std::string name = "";
};
-struct SExecRequestedRule {
- std::string szRule = "";
- uint64_t iPid = 0;
-};
-
enum eConfigOptionType : uint8_t {
CONFIG_OPTION_BOOL = 0,
CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/
@@ -214,7 +207,6 @@ class CConfigManager {
bool deviceConfigExplicitlySet(const std::string&, const std::string&);
bool deviceConfigExists(const std::string&);
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&);
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
@@ -229,8 +221,6 @@ class CConfigManager {
std::string getBoundMonitorStringForWS(const std::string&);
const std::vector& getAllWorkspaceRules();
- std::vector> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
- std::vector> getMatchingRules(PHLLS);
void ensurePersistentWorkspacesPresent();
const std::vector& getAllDescriptions();
@@ -260,8 +250,6 @@ class CConfigManager {
SP getAnimationPropertyConfig(const std::string&);
- void addExecRule(const SExecRequestedRule&);
-
void handlePluginLoads();
std::string getErrors();
@@ -274,22 +262,24 @@ class CConfigManager {
std::optional handleMonitor(const std::string&, const std::string&);
std::optional handleBind(const std::string&, const std::string&);
std::optional handleUnbind(const std::string&, const std::string&);
- std::optional handleWindowRule(const std::string&, const std::string&);
- std::optional handleLayerRule(const std::string&, const std::string&);
std::optional handleWorkspaceRules(const std::string&, const std::string&);
std::optional handleBezier(const std::string&, const std::string&);
std::optional handleAnimation(const std::string&, const std::string&);
std::optional handleSource(const std::string&, const std::string&);
std::optional handleSubmap(const std::string&, const std::string&);
- std::optional handleBlurLS(const std::string&, const std::string&);
std::optional handleBindWS(const std::string&, const std::string&);
std::optional handleEnv(const std::string&, const std::string&);
std::optional handlePlugin(const std::string&, const std::string&);
std::optional handlePermission(const std::string&, const std::string&);
std::optional handleGesture(const std::string&, const std::string&);
+ std::optional handleWindowrule(const std::string&, const std::string&);
+ std::optional handleLayerrule(const std::string&, const std::string&);
std::optional handleMonitorv2(const std::string& output);
Hyprlang::CParseResult handleMonitorv2();
+ std::optional addRuleFromConfigKey(const std::string& name);
+ std::optional addLayerRuleFromConfigKey(const std::string& name);
+ Hyprlang::CParseResult reloadRules();
std::string m_configCurrentPath;
@@ -310,19 +300,16 @@ class CConfigManager {
SSubmap m_currentSubmap;
- std::vector m_execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
-
std::vector m_declaredPlugins;
std::vector m_pluginKeywords;
std::vector m_pluginVariables;
+ std::vector> m_keywordRules;
+
bool m_isFirstLaunch = true; // For exec-once
std::vector m_monitorRules;
std::vector m_workspaceRules;
- std::vector> m_windowRules;
- std::vector> m_layerRules;
- std::vector m_blurLSNamespaces;
bool m_firstExecDispatched = false;
bool m_manualCrashInitiated = false;
@@ -336,11 +323,11 @@ class CConfigManager {
uint32_t m_configValueNumber = 0;
// internal methods
- void updateBlurredLS(const std::string&, const bool);
void setDefaultAnimationVars();
std::optional resetHLConfig();
std::optional generateConfig(std::string configPath);
std::optional verifyConfigExists();
+ void reloadRuleConfigs();
void postConfigReload(const Hyprlang::CParseResult& result);
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
diff --git a/src/config/ConfigWatcher.cpp b/src/config/ConfigWatcher.cpp
index f3a8a2cad..dfd84b43b 100644
--- a/src/config/ConfigWatcher.cpp
+++ b/src/config/ConfigWatcher.cpp
@@ -1,5 +1,7 @@
#include "ConfigWatcher.hpp"
+#if defined(__linux__)
#include
+#endif
#include
#include "../debug/Log.hpp"
#include
diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp
index b5de65030..82a697157 100644
--- a/src/debug/HyprCtl.cpp
+++ b/src/debug/HyprCtl.cpp
@@ -44,6 +44,7 @@ using namespace Hyprutils::OS;
#include "config/ConfigManager.hpp"
#include "helpers/MiscFunctions.hpp"
#include "../desktop/LayerSurface.hpp"
+#include "../desktop/rule/Engine.hpp"
#include "../version.h"
#include "../Compositor.hpp"
@@ -317,7 +318,7 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ
}
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)
return std::ranges::fold_left(tags, std::string(),
@@ -1272,8 +1273,12 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
if (COMMAND.empty())
return "Invalid input: command is empty";
+ g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = true;
+
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 (COMMAND == "monitor" || COMMAND == "source")
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
@@ -1306,8 +1311,7 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
g_pConfigManager->updateWatcher();
// decorations will probably need a repaint
- if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
- COMMAND.starts_with("windowrule")) {
+ if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") {
static auto PZOOMFACTOR = CConfigValue("cursor:zoom_factor");
for (auto const& m : g_pCompositor->m_monitors) {
*(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"))
g_pConfigManager->ensurePersistentWorkspacesPresent();
@@ -1521,11 +1528,6 @@ static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string req
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) {
CVarList vars(request, 0, ' ');
@@ -1543,9 +1545,9 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const bool FORMNORM = format == FORMAT_NORMAL;
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)
- sizeValue = PWINDOW->m_windowData.maxSize.valueOr(Vector2D(INFINITY, INFINITY));
+ sizeValue = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D(INFINITY, INFINITY));
if (FORMNORM)
return std::format("{} {}", sizeValue.x, sizeValue.y);
@@ -1556,7 +1558,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
}
};
- auto alphaToString = [&](CWindowOverridableVar& alpha, bool getAlpha) -> std::string {
+ auto alphaToString = [&](Desktop::Types::COverridableVar& alpha, bool getAlpha) -> std::string {
if (FORMNORM) {
if (getAlpha)
return std::format("{}", alpha.valueOrDefault().alpha);
@@ -1590,7 +1592,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
const auto* const ACTIVECOLOR =
!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)
return borderColorString;
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) :
(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)
return borderColorString;
else
@@ -1618,38 +1620,92 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
};
- if (PROP == "animationstyle") {
- auto& animationStyle = PWINDOW->m_windowData.animationStyle;
+ if (PROP == "animation") {
+ auto& animationStyle = PWINDOW->m_ruleApplicator->animationStyle();
if (FORMNORM)
return animationStyle.valueOr("(unset)");
else
return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
- } else if (PROP == "maxsize")
+ } else if (PROP == "max_size")
return sizeToString(true);
- else if (PROP == "minsize")
+ else if (PROP == "min_size")
return sizeToString(false);
- else if (PROP == "alpha")
- return alphaToString(PWINDOW->m_windowData.alpha, true);
- else if (PROP == "alphainactive")
- return alphaToString(PWINDOW->m_windowData.alphaInactive, true);
- else if (PROP == "alphafullscreen")
- return alphaToString(PWINDOW->m_windowData.alphaFullscreen, true);
- else if (PROP == "alphaoverride")
- return alphaToString(PWINDOW->m_windowData.alpha, false);
- else if (PROP == "alphainactiveoverride")
- return alphaToString(PWINDOW->m_windowData.alphaInactive, false);
- else if (PROP == "alphafullscreenoverride")
- return alphaToString(PWINDOW->m_windowData.alphaFullscreen, false);
- else if (PROP == "activebordercolor")
+ else if (PROP == "opacity")
+ return alphaToString(PWINDOW->m_ruleApplicator->alpha(), true);
+ else if (PROP == "opacity_inactive")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), true);
+ else if (PROP == "opacity_fullscreen")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), true);
+ else if (PROP == "opacity_override")
+ return alphaToString(PWINDOW->m_ruleApplicator->alpha(), false);
+ else if (PROP == "opacity_inactive_override")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), false);
+ else if (PROP == "opacity_fullscreen_override")
+ return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), false);
+ else if (PROP == "active_border_color")
return borderColorToString(true);
- else if (PROP == "inactivebordercolor")
+ else if (PROP == "inactive_border_color")
return borderColorToString(false);
- else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end())
- return windowPropToString(*search->second(PWINDOW));
- else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end())
- return windowPropToString(*search->second(PWINDOW));
- else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end())
- return windowPropToString(*search->second(PWINDOW));
+ else if (PROP == "allows_input")
+ return windowPropToString(PWINDOW->m_ruleApplicator->allowsInput());
+ else if (PROP == "decorate")
+ return windowPropToString(PWINDOW->m_ruleApplicator->decorate());
+ else if (PROP == "focus_on_activate")
+ 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";
}
@@ -2014,7 +2070,6 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
- registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp});
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
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())
continue;
- w->updateDynamicRules();
- g_pCompositor->updateWindowAnimatedDecorationValues(w);
+ Desktop::Rule::ruleEngine()->updateAllRules();
}
for (auto const& m : g_pCompositor->m_monitors) {
diff --git a/src/debug/HyprCtl.hpp b/src/debug/HyprCtl.hpp
index 95bb65b8c..d4f7aa149 100644
--- a/src/debug/HyprCtl.hpp
+++ b/src/debug/HyprCtl.hpp
@@ -25,9 +25,10 @@ class CHyprCtl {
Hyprutils::OS::CFileDescriptor m_socketFD;
struct {
- bool all = false;
- bool sysInfoConfig = false;
- pid_t pid = 0;
+ bool all = false;
+ bool sysInfoConfig = false;
+ bool isDynamicKeyword = false;
+ pid_t pid = 0;
SP> pendingPromise;
} m_currentRequestParams;
diff --git a/src/desktop/LayerRule.cpp b/src/desktop/LayerRule.cpp
deleted file mode 100644
index d6bfcadf2..000000000
--- a/src/desktop/LayerRule.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include
-#include "LayerRule.hpp"
-#include
-#include
-#include "../debug/Log.hpp"
-
-static const auto RULES = std::unordered_set{"noanim", "blur", "blurpopups", "dimaround", "noscreenshare"};
-static const auto RULES_PREFIX = std::unordered_set{"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;
- }
-}
diff --git a/src/desktop/LayerRule.hpp b/src/desktop/LayerRule.hpp
deleted file mode 100644
index 7b6c8a6d9..000000000
--- a/src/desktop/LayerRule.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include
-#include
-#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;
-};
diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp
index 6278078d9..aab0b15ac 100644
--- a/src/desktop/LayerSurface.cpp
+++ b/src/desktop/LayerSurface.cpp
@@ -37,7 +37,7 @@ PHLLS CLayerSurface::create(SP resource) {
pLS->m_monitor = pMonitor;
pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS);
- pLS->m_forceBlur = g_pConfigManager->shouldBlurLS(pLS->m_namespace);
+ pLS->m_ruleApplicator = makeUnique(pLS);
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);
@@ -55,7 +55,7 @@ PHLLS CLayerSurface::create(SP resource) {
void CLayerSurface::registerCallbacks() {
m_alpha->setUpdateCallback([this](auto) {
- if (m_dimAround && m_monitor)
+ if (m_ruleApplicator->dimAround().valueOrDefault() && m_monitor)
g_pHyprRenderer->damageMonitor(m_monitor.lock());
});
}
@@ -137,6 +137,8 @@ void CLayerSurface::onMap() {
m_mapped = true;
m_interactivity = m_layerSurface->m_current.interactivity;
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL);
+
m_layerSurface->m_surface->map();
// this layer might be re-mapped.
@@ -149,8 +151,6 @@ void CLayerSurface::onMap() {
if (!PMONITOR)
return;
- applyRules();
-
PMONITOR->m_scheduledRecalc = true;
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
@@ -398,83 +398,6 @@ void CLayerSurface::onCommit() {
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() {
if (!m_fadingOut)
return false;
diff --git a/src/desktop/LayerSurface.hpp b/src/desktop/LayerSurface.hpp
index b70739cd3..5676e4d21 100644
--- a/src/desktop/LayerSurface.hpp
+++ b/src/desktop/LayerSurface.hpp
@@ -3,6 +3,7 @@
#include
#include "../defines.hpp"
#include "WLSurface.hpp"
+#include "rule/layerRule/LayerRuleApplicator.hpp"
#include "../helpers/AnimatedVariable.hpp"
class CLayerShellResource;
@@ -17,7 +18,6 @@ class CLayerSurface {
public:
~CLayerSurface();
- void applyRules();
bool isFadedOut();
int popupsCount();
@@ -28,47 +28,35 @@ class CLayerSurface {
WP m_layerSurface;
// the header providing the enum type cannot be imported here
- int m_interactivity = 0;
+ int m_interactivity = 0;
- SP m_surface;
+ SP m_surface;
- bool m_mapped = false;
- uint32_t m_layer = 0;
+ bool m_mapped = false;
+ uint32_t m_layer = 0;
- PHLMONITORREF m_monitor;
+ PHLMONITORREF m_monitor;
- bool m_fadingOut = false;
- bool m_readyToDelete = false;
- bool m_noProcess = false;
- bool m_noAnimations = false;
+ bool m_fadingOut = false;
+ bool m_readyToDelete = false;
+ bool m_noProcess = false;
- bool m_forceBlur = false;
- 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;
+ UP m_ruleApplicator;
- std::optional m_animationStyle;
+ PHLLSREF m_self;
- PHLLSREF m_self;
+ CBox m_geometry = {0, 0, 0, 0};
+ Vector2D m_position;
+ std::string m_namespace = "";
+ UP m_popupHead;
- CBox m_geometry = {0, 0, 0, 0};
- Vector2D m_position;
- std::string m_namespace = "";
- UP m_popupHead;
+ pid_t getPID();
- pid_t getPID();
-
- void onDestroy();
- void onMap();
- void onUnmap();
- void onCommit();
- MONITORID monitorID();
+ void onDestroy();
+ void onMap();
+ void onUnmap();
+ void onCommit();
+ MONITORID monitorID();
private:
struct {
diff --git a/src/desktop/Rule.cpp b/src/desktop/Rule.cpp
deleted file mode 100644
index 93f38de03..000000000
--- a/src/desktop/Rule.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include
-#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(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;
-}
\ No newline at end of file
diff --git a/src/desktop/Rule.hpp b/src/desktop/Rule.hpp
deleted file mode 100644
index 9d3de70e8..000000000
--- a/src/desktop/Rule.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include
-
-//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 m_regex;
- bool m_negative = false;
-};
\ No newline at end of file
diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp
index 8671c0036..b27128869 100644
--- a/src/desktop/Window.cpp
+++ b/src/desktop/Window.cpp
@@ -24,6 +24,7 @@
#include "../protocols/FractionalScale.hpp"
#include "../xwayland/XWayland.hpp"
#include "../helpers/Color.hpp"
+#include "../helpers/math/Expression.hpp"
#include "../events/Events.hpp"
#include "../managers/XWaylandManager.hpp"
#include "../render/Renderer.hpp"
@@ -41,8 +42,9 @@ using enum NContentType::eContentType;
PHLWINDOW CWindow::create(SP surface) {
PHLWINDOW pWindow = SP(new CWindow(surface));
- pWindow->m_self = pWindow;
- pWindow->m_isX11 = true;
+ pWindow->m_self = pWindow;
+ pWindow->m_isX11 = true;
+ pWindow->m_ruleApplicator = makeUnique(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_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
@@ -67,6 +69,7 @@ PHLWINDOW CWindow::create(SP resource) {
pWindow->m_self = pWindow;
resource->m_toplevel->m_window = pWindow;
+ pWindow->m_ruleApplicator = makeUnique(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_realSize, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE);
@@ -138,7 +141,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
const int BORDERSIZE = getRealBorderSize();
- if (m_windowData.dimAround.valueOrDefault()) {
+ if (m_ruleApplicator->dimAround().valueOrDefault()) {
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},
.bottomRight = {PMONITOR->m_size.x - (m_realPosition->value().x - PMONITOR->m_position.x),
@@ -191,7 +194,7 @@ SBoxExtents CWindow::getFullWindowExtents() {
}
CBox CWindow::getFullWindowBoundingBox() {
- if (m_windowData.dimAround.valueOrDefault()) {
+ if (m_ruleApplicator->dimAround().valueOrDefault()) {
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};
}
@@ -251,7 +254,7 @@ SBoxExtents CWindow::getWindowExtentsUnified(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();
if (PMONITOR)
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;
}
-void CWindow::applyDynamicRule(const SP& r) {
- const eOverridePriority priority = r->m_execRule ? PRIORITY_SET_PROP : PRIORITY_WINDOW_RULE;
- static auto PCLAMP_TILED = CConfigValue("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(*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(*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(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(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
// it is assumed that the point is within the real window box (m_vRealPosition, m_vRealSize)
// otherwise behaviour is undefined
@@ -924,6 +711,8 @@ void CWindow::createGroup() {
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("1,{:x}", rc(this))});
}
+
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
}
void CWindow::destroyGroup() {
@@ -943,6 +732,7 @@ void CWindow::destroyGroup() {
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{:x}", rc(this))});
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
return;
}
@@ -969,6 +759,7 @@ void CWindow::destroyGroup() {
g_pKeybindManager->m_groupsLocked = true;
for (auto const& w : members) {
g_pLayoutManager->getCurrentLayout()->onWindowCreated(w);
+ w->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
w->updateWindowDecos();
}
g_pKeybindManager->m_groupsLocked = GROUPSLOCKEDPREV;
@@ -982,6 +773,8 @@ void CWindow::destroyGroup() {
if (!addresses.empty())
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)});
}
@@ -1217,16 +1010,16 @@ float CWindow::rounding() {
static auto PROUNDING = CConfigValue("decoration:rounding");
static auto PROUNDINGPOWER = CConfigValue("decoration:rounding_power");
- float roundingPower = m_windowData.roundingPower.valueOr(*PROUNDINGPOWER);
- float rounding = m_windowData.rounding.valueOr(*PROUNDING) * (roundingPower / 2.0); /* Make perceived roundness consistent. */
+ float roundingPower = m_ruleApplicator->roundingPower().valueOr(*PROUNDINGPOWER);
+ 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() {
static auto PROUNDINGPOWER = CConfigValue("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() {
@@ -1236,50 +1029,43 @@ void CWindow::updateWindowData() {
}
void CWindow::updateWindowData(const SWorkspaceRule& workspaceRule) {
- static auto PNOBORDERONFLOATING = CConfigValue("general:no_border_on_floating");
-
- if (*PNOBORDERONFLOATING)
- m_windowData.noBorder = CWindowOverridableVar(m_isFloating, PRIORITY_LAYOUT);
- else
- 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);
+ m_ruleApplicator->borderSize().matchOptional(workspaceRule.borderSize, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->decorate().matchOptional(workspaceRule.decorate, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->borderSize().matchOptional(workspaceRule.noBorder ? std::optional(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->rounding().matchOptional(workspaceRule.noRounding.value_or(false) ? std::optional(0) : std::nullopt, Desktop::Types::PRIORITY_WORKSPACE_RULE);
+ m_ruleApplicator->noShadow().matchOptional(workspaceRule.noShadow, Desktop::Types::PRIORITY_WORKSPACE_RULE);
}
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;
static auto PBORDERSIZE = CConfigValue("general:border_size");
- return m_windowData.borderSize.valueOr(*PBORDERSIZE);
+ return m_ruleApplicator->borderSize().valueOr(*PBORDERSIZE);
}
float CWindow::getScrollMouse() {
static auto PINPUTSCROLLFACTOR = CConfigValue("input:scroll_factor");
- return m_windowData.scrollMouse.valueOr(*PINPUTSCROLLFACTOR);
+ return m_ruleApplicator->scrollMouse().valueOr(*PINPUTSCROLLFACTOR);
}
float CWindow::getScrollTouchpad() {
static auto PTOUCHPADSCROLLFACTOR = CConfigValue("input:touchpad:scroll_factor");
- return m_windowData.scrollTouchpad.valueOr(*PTOUCHPADSCROLLFACTOR);
+ return m_ruleApplicator->scrollTouchpad().valueOr(*PTOUCHPADSCROLLFACTOR);
}
bool CWindow::isScrollMouseOverridden() {
- return m_windowData.scrollMouse.hasValue();
+ return m_ruleApplicator->scrollMouse().hasValue();
}
bool CWindow::isScrollTouchpadOverridden() {
- return m_windowData.scrollTouchpad.hasValue();
+ return m_ruleApplicator->scrollTouchpad().hasValue();
}
bool CWindow::canBeTorn() {
static auto PTEARING = CConfigValue("general:allow_tearing");
- return m_windowData.tearing.valueOr(m_tearingHint) && *PTEARING;
+ return m_ruleApplicator->tearing().valueOr(m_tearingHint) && *PTEARING;
}
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(this))});
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;
if (!m_isMapped) {
@@ -1539,8 +1326,7 @@ void CWindow::onUpdateMeta() {
}
if (doUpdate) {
- updateDynamicRules();
- g_pCompositor->updateWindowAnimatedDecorationValues(m_self.lock());
+ m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_TITLE | Desktop::Rule::RULE_PROP_CLASS);
updateToplevel();
}
}
@@ -1718,18 +1504,6 @@ PHLWINDOW CWindow::getSwallower() {
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() {
return m_xwaylandSurface && m_xwaylandSurface->m_overrideRedirect;
}
@@ -1753,7 +1527,7 @@ Vector2D CWindow::requestedMinSize() {
Vector2D CWindow::requestedMaxSize() {
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);
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_reportedSize;
}
+
+void CWindow::updateDecorationValues() {
+ static auto PACTIVECOL = CConfigValue("general:col.active_border");
+ static auto PINACTIVECOL = CConfigValue("general:col.inactive_border");
+ static auto PNOGROUPACTIVECOL = CConfigValue("general:col.nogroup_border_active");
+ static auto PNOGROUPINACTIVECOL = CConfigValue("general:col.nogroup_border");
+ static auto PGROUPACTIVECOL = CConfigValue("group:col.border_active");
+ static auto PGROUPINACTIVECOL = CConfigValue("group:col.border_inactive");
+ static auto PGROUPACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_active");
+ static auto PGROUPINACTIVELOCKEDCOL = CConfigValue("group:col.border_locked_inactive");
+ static auto PINACTIVEALPHA = CConfigValue("decoration:inactive_opacity");
+ static auto PACTIVEALPHA = CConfigValue("decoration:active_opacity");
+ static auto PFULLSCREENALPHA = CConfigValue("decoration:fullscreen_opacity");
+ static auto PSHADOWCOL = CConfigValue("decoration:shadow:color");
+ static auto PSHADOWCOLINACTIVE = CConfigValue("decoration:shadow:color_inactive");
+ static auto PDIMSTRENGTH = CConfigValue("decoration:dim_strength");
+ static auto PDIMENABLED = CConfigValue("decoration:dim_inactive");
+ static auto PDIMMODAL = CConfigValue("decoration:dim_modal");
+
+ auto* const ACTIVECOL = sc((PACTIVECOL.ptr())->getData());
+ auto* const INACTIVECOL = sc((PINACTIVECOL.ptr())->getData());
+ auto* const NOGROUPACTIVECOL = sc((PNOGROUPACTIVECOL.ptr())->getData());
+ auto* const NOGROUPINACTIVECOL = sc((PNOGROUPINACTIVECOL.ptr())->getData());
+ auto* const GROUPACTIVECOL = sc((PGROUPACTIVECOL.ptr())->getData());
+ auto* const GROUPINACTIVECOL = sc((PGROUPINACTIVECOL.ptr())->getData());
+ auto* const GROUPACTIVELOCKEDCOL = sc((PGROUPACTIVELOCKEDCOL.ptr())->getData());
+ auto* const GROUPINACTIVELOCKEDCOL = sc((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 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 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};
+}
diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp
index 0a7e207c3..63492682a 100644
--- a/src/desktop/Window.hpp
+++ b/src/desktop/Window.hpp
@@ -16,20 +16,12 @@
#include "Subsurface.hpp"
#include "WLSurface.hpp"
#include "Workspace.hpp"
-#include "WindowRule.hpp"
-#include "WindowOverridableVar.hpp"
+#include "rule/windowRule/WindowRuleApplicator.hpp"
#include "../protocols/types/ContentType.hpp"
class CXDGSurfaceResource;
class CXWaylandSurface;
-enum eIdleInhibitMode : uint8_t {
- IDLEINHIBIT_NONE = 0,
- IDLEINHIBIT_ALWAYS,
- IDLEINHIBIT_FULLSCREEN,
- IDLEINHIBIT_FOCUS
-};
-
enum eGroupRules : uint8_t {
// effective only during first map, except for _ALWAYS variant
GROUP_NONE = 0,
@@ -65,65 +57,6 @@ enum eSuppressEvents : uint8_t {
class IWindowTransformer;
-struct SAlphaValue {
- float alpha;
- bool overridden;
-
- float applyAlpha(float a) const {
- if (overridden)
- return alpha;
- else
- return alpha * a;
- };
-};
-
-struct SWindowData {
- CWindowOverridableVar alpha = SAlphaValue{.alpha = 1.f, .overridden = false};
- CWindowOverridableVar alphaInactive = SAlphaValue{.alpha = 1.f, .overridden = false};
- CWindowOverridableVar alphaFullscreen = SAlphaValue{.alpha = 1.f, .overridden = false};
-
- CWindowOverridableVar allowsInput = false;
- CWindowOverridableVar dimAround = false;
- CWindowOverridableVar decorate = true;
- CWindowOverridableVar focusOnActivate = false;
- CWindowOverridableVar keepAspectRatio = false;
- CWindowOverridableVar nearestNeighbor = false;
- CWindowOverridableVar noAnim = false;
- CWindowOverridableVar noBorder = false;
- CWindowOverridableVar noBlur = false;
- CWindowOverridableVar noDim = false;
- CWindowOverridableVar noFocus = false;
- CWindowOverridableVar noMaxSize = false;
- CWindowOverridableVar noRounding = false;
- CWindowOverridableVar noShadow = false;
- CWindowOverridableVar noShortcutsInhibit = false;
- CWindowOverridableVar opaque = false;
- CWindowOverridableVar RGBX = false;
- CWindowOverridableVar syncFullscreen = true;
- CWindowOverridableVar tearing = false;
- CWindowOverridableVar xray = false;
- CWindowOverridableVar renderUnfocused = false;
- CWindowOverridableVar noFollowMouse = false;
- CWindowOverridableVar noScreenShare = false;
- CWindowOverridableVar noVRR = false;
-
- CWindowOverridableVar borderSize = {std::string("general:border_size"), sc(0), std::nullopt};
- CWindowOverridableVar rounding = {std::string("decoration:rounding"), sc