mirror of
https://github.com/hyprwm/Hyprland
synced 2025-12-24 19:10:04 +01:00
Compare commits
No commits in common. "main" and "v0.51.0" have entirely different histories.
401 changed files with 9835 additions and 18989 deletions
109
.clang-tidy
109
.clang-tidy
|
|
@ -1,111 +1,4 @@
|
|||
WarningsAsErrors: >
|
||||
-*,
|
||||
bugprone-*,
|
||||
-bugprone-multi-level-implicit-pointer-conversion,
|
||||
-bugprone-empty-catch,
|
||||
-bugprone-unused-return-value,
|
||||
-bugprone-reserved-identifier,
|
||||
-bugprone-switch-missing-default-case,
|
||||
-bugprone-unused-local-non-trivial-variable,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-forward-declararion-namespace,
|
||||
-bugprone-forward-declararion-namespace,
|
||||
-bugprone-macro-parentheses,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-assignment-in-if-condition,
|
||||
concurrency-*,
|
||||
-concurrency-mt-unsafe,
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-pro-type-const-cast,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-avoid-goto,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-avoid-do-while,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-cppcoreguidelines-explicit-virtual-functions,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-member-init,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-macro-to-enum,
|
||||
-cppcoreguidelines-init-variables,
|
||||
-cppcoreguidelines-pro-type-cstyle-cast,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-google-global-names-in-headers,
|
||||
-google-readability-casting,
|
||||
google-runtime-operator,
|
||||
misc-*,
|
||||
-misc-use-internal-linkage,
|
||||
-misc-unused-parameters,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-include-cleaner,
|
||||
-misc-use-anonymous-namespace,
|
||||
-misc-const-correctness,
|
||||
modernize-*,
|
||||
-modernize-use-emplace,
|
||||
-modernize-redundant-void-arg,
|
||||
-modernize-use-starts-ends-with,
|
||||
-modernize-use-designated-initializers,
|
||||
-modernize-use-std-numbers,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-use-using,
|
||||
-modernize-use-override,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-macro-to-enum,
|
||||
-modernize-loop-convert,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-use-auto,
|
||||
performance-*,
|
||||
-performance-inefficient-vector-operation,
|
||||
-performance-inefficient-string-concatenation,
|
||||
-performance-enum-size,
|
||||
-performance-move-const-arg,
|
||||
-performance-avoid-endl,
|
||||
-performance-unnecessary-value-param,
|
||||
portability-std-allocator-const,
|
||||
readability-*,
|
||||
-readability-identifier-naming,
|
||||
-readability-use-std-min-max,
|
||||
-readability-math-missing-parentheses,
|
||||
-readability-simplify-boolean-expr,
|
||||
-readability-static-accessed-through-instance,
|
||||
-readability-use-anyofallof,
|
||||
-readability-enum-initial-value,
|
||||
-readability-redundant-inline-specifier,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-function-size,
|
||||
-readability-identifier-length,
|
||||
-readability-magic-numbers,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-braces-around-statements,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-else-after-return,
|
||||
-readability-container-data-pointer,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-avoid-nested-conditional-operator,
|
||||
-readability-redundant-member-init,
|
||||
-readability-redundant-string-init,
|
||||
-readability-avoid-const-params-in-decls,
|
||||
-readability-named-parameter,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-qualified-auto,
|
||||
-readability-make-member-function-const,
|
||||
-readability-isolate-declaration,
|
||||
-readability-inconsistent-declaration-parameter-name,
|
||||
-clang-diagnostic-error,
|
||||
|
||||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '.*\.hpp'
|
||||
FormatStyle: file
|
||||
Checks: >
|
||||
|
|
|
|||
15
.github/actions/setup_base/action.yml
vendored
15
.github/actions/setup_base/action.yml
vendored
|
|
@ -24,7 +24,6 @@ runs:
|
|||
glm \
|
||||
glslang \
|
||||
go \
|
||||
gtest \
|
||||
hyprlang \
|
||||
hyprcursor \
|
||||
jq \
|
||||
|
|
@ -46,7 +45,6 @@ runs:
|
|||
libxkbfile \
|
||||
lld \
|
||||
meson \
|
||||
muparser \
|
||||
ninja \
|
||||
pango \
|
||||
pixman \
|
||||
|
|
@ -76,25 +74,16 @@ runs:
|
|||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
cmake --install build
|
||||
|
||||
- name: Get hyprwire-git
|
||||
- name: Get hyprgraphics-git
|
||||
shell: bash
|
||||
run: |
|
||||
git clone https://github.com/hyprwm/hyprwire --recursive
|
||||
cd hyprwire
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
cmake --install build
|
||||
git clone https://github.com/hyprwm/hyprgraphics && cd hyprgraphics && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprgraphics && cmake --install build
|
||||
|
||||
- name: Get hyprutils-git
|
||||
shell: bash
|
||||
run: |
|
||||
git clone https://github.com/hyprwm/hyprutils && cd hyprutils && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprutils && cmake --install build
|
||||
|
||||
- name: Get hyprgraphics-git
|
||||
shell: bash
|
||||
run: |
|
||||
git clone https://github.com/hyprwm/hyprgraphics && cd hyprgraphics && cmake -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -B build && cmake --build build --target hyprgraphics && cmake --install build
|
||||
|
||||
- name: Get aquamarine-git
|
||||
shell: bash
|
||||
run: |
|
||||
|
|
|
|||
4
.github/labeler.yml
vendored
4
.github/labeler.yml
vendored
|
|
@ -22,10 +22,6 @@ protocols:
|
|||
- changed-files:
|
||||
- any-glob-to-any-file: ["protocols/**", "src/protocols/**"]
|
||||
|
||||
start:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "start/**"
|
||||
|
||||
core:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "src/**"
|
||||
|
|
|
|||
85
.github/workflows/ci.yaml
vendored
85
.github/workflows/ci.yaml
vendored
|
|
@ -21,16 +21,19 @@ jobs:
|
|||
|
||||
- name: Build Hyprland
|
||||
run: |
|
||||
CFLAGS=-Werror CXXFLAGS=-Werror make nopch
|
||||
CFLAGS=-Werror CXXFLAGS=-Werror make all
|
||||
|
||||
- name: Compress and package artifacts
|
||||
run: |
|
||||
mkdir x86_64-pc-linux-gnu
|
||||
mkdir hyprland
|
||||
mkdir hyprland/example
|
||||
mkdir hyprland/assets
|
||||
cp ./LICENSE hyprland/
|
||||
cp build/Hyprland hyprland/
|
||||
cp build/hyprctl/hyprctl hyprland/
|
||||
cp build/hyprpm/hyprpm hyprland/
|
||||
cp build/Hyprland hyprland/
|
||||
cp -r example/ hyprland/
|
||||
cp -r assets/ hyprland/
|
||||
tar -cvJf Hyprland.tar.xz hyprland
|
||||
|
|
@ -41,16 +44,86 @@ 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"
|
||||
name: "Code Style (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: archlinux
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
- 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: clang-format check
|
||||
uses: jidicula/clang-format-action@v4.16.0
|
||||
with:
|
||||
exclude-regex: ^subprojects$
|
||||
run: ninja -C build clang-format-check
|
||||
|
|
|
|||
36
.github/workflows/clang-format.yml
vendored
36
.github/workflows/clang-format.yml
vendored
|
|
@ -4,23 +4,43 @@ jobs:
|
|||
clang-format:
|
||||
permissions: write-all
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Code Style"
|
||||
name: "Code Style (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: archlinux
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
- 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: clang-format check
|
||||
uses: jidicula/clang-format-action@v4.16.0
|
||||
with:
|
||||
exclude-regex: ^subprojects$
|
||||
run: ninja -C build clang-format-check
|
||||
|
||||
- name: Create comment
|
||||
- name: clang-format apply
|
||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||
run: ninja -C build clang-format
|
||||
|
||||
- name: Create patch
|
||||
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).' > clang-format.patch
|
||||
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 '<details>' >> clang-format.patch
|
||||
echo '<summary>clang-format.patch</summary>' >> clang-format.patch
|
||||
echo >> clang-format.patch
|
||||
echo '```diff' >> clang-format.patch
|
||||
git diff >> clang-format.patch
|
||||
echo '```' >> clang-format.patch
|
||||
echo >> clang-format.patch
|
||||
echo '</details>' >> clang-format.patch
|
||||
|
||||
- name: Post comment
|
||||
- name: Comment patch
|
||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||
uses: mshick/add-pr-comment@v2
|
||||
with:
|
||||
|
|
|
|||
45
.github/workflows/new-pr-comment.yml
vendored
45
.github/workflows/new-pr-comment.yml
vendored
|
|
@ -1,45 +0,0 @@
|
|||
name: "New MR welcome comment"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
if: >
|
||||
github.event.pull_request.user.login != 'vaxerski' &&
|
||||
github.event.pull_request.user.login != 'fufexan' &&
|
||||
github.event.pull_request.user.login != 'gulafaran' &&
|
||||
github.event.pull_request.user.login != 'ujint34' &&
|
||||
github.event.pull_request.user.login != 'paideiadilemma' &&
|
||||
github.event.pull_request.user.login != 'notashelf'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
PR_COMMENT: |
|
||||
Hello and thank you for making a PR to Hyprland!
|
||||
|
||||
Please check the [PR Guidelines](https://wiki.hypr.land/Contributing-and-Debugging/PR-Guidelines/) and make sure your PR follows them.
|
||||
It will make the entire review process faster. :)
|
||||
|
||||
If your code can be tested, please always add tests. See more [here](https://wiki.hypr.land/Contributing-and-Debugging/Tests/).
|
||||
|
||||
_beep boop, I'm just a bot. A real human will review your PR soon._
|
||||
|
||||
steps:
|
||||
- name: Add comment to PR
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body: process.env.PR_COMMENT,
|
||||
});
|
||||
5
.github/workflows/nix-ci.yml
vendored
5
.github/workflows/nix-ci.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
uses: ./.github/workflows/nix.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
command: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}' -L --extra-substituters "https://hyprland.cachix.org"
|
||||
command: nix build 'github:hyprwm/Hyprland?ref=${{ github.ref }}' -L --extra-substituters "https://hyprland.cachix.org"
|
||||
|
||||
xdph:
|
||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
|
||||
|
|
@ -21,9 +21,10 @@ jobs:
|
|||
uses: ./.github/workflows/nix.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
command: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}#xdg-desktop-portal-hyprland' -L --extra-substituters "https://hyprland.cachix.org"
|
||||
command: nix build 'github:hyprwm/Hyprland?ref=${{ github.ref }}#xdg-desktop-portal-hyprland' -L --extra-substituters "https://hyprland.cachix.org"
|
||||
|
||||
test:
|
||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
|
||||
needs: hyprland
|
||||
uses: ./.github/workflows/nix-test.yml
|
||||
secrets: inherit
|
||||
|
|
|
|||
18
.github/workflows/nix-test.yml
vendored
18
.github/workflows/nix-test.yml
vendored
|
|
@ -20,13 +20,25 @@ jobs:
|
|||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key (per job)
|
||||
primary-key: nix-${{ runner.os }}-${{ github.job }}
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}
|
||||
# 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:
|
||||
|
|
@ -34,7 +46,7 @@ jobs:
|
|||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Run test VM
|
||||
run: nix build 'github:${{ github.repository }}?ref=${{ github.ref }}#checks.x86_64-linux.tests' -L --extra-substituters "https://hyprland.cachix.org"
|
||||
run: nix build 'github:hyprwm/Hyprland?ref=${{ github.ref }}#checks.x86_64-linux.tests' -L --extra-substituters "https://hyprland.cachix.org"
|
||||
|
||||
- name: Check exit status
|
||||
run: grep 0 result/exit_status
|
||||
|
|
|
|||
20
.github/workflows/nix-update-inputs.yml
vendored
20
.github/workflows/nix-update-inputs.yml
vendored
|
|
@ -27,13 +27,25 @@ jobs:
|
|||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key (per job)
|
||||
primary-key: nix-${{ runner.os }}-${{ github.job }}
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
|
||||
# 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
|
||||
gc-max-store-size-linux: 5G
|
||||
# 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
|
||||
|
||||
- name: Update inputs
|
||||
run: nix/update-inputs.sh
|
||||
|
|
|
|||
16
.github/workflows/nix.yml
vendored
16
.github/workflows/nix.yml
vendored
|
|
@ -25,13 +25,25 @@ jobs:
|
|||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key (per job)
|
||||
primary-key: nix-${{ runner.os }}-${{ github.job }}
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}
|
||||
# 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:
|
||||
|
|
|
|||
33
.github/workflows/release.yaml
vendored
33
.github/workflows/release.yaml
vendored
|
|
@ -9,36 +9,17 @@ jobs:
|
|||
source-tarball:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
- name: Checkout Hyprland
|
||||
id: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Populate git info in version.h.in
|
||||
- name: Generate version
|
||||
id: genversion
|
||||
run: |
|
||||
git fetch --tags --unshallow || true
|
||||
|
||||
COMMIT_HASH=$(git rev-parse HEAD)
|
||||
BRANCH="${GITHUB_REF_NAME:-$(git rev-parse --abbrev-ref HEAD)}"
|
||||
COMMIT_MSG=$(git show -s --format=%s | sed 's/[&/]/\\&/g')
|
||||
COMMIT_DATE=$(git show -s --format=%cd --date=local)
|
||||
GIT_DIRTY=$(git diff-index --quiet HEAD -- && echo "clean" || echo "dirty")
|
||||
GIT_TAG=$(git describe --tags --always || echo "unknown")
|
||||
GIT_COMMITS=$(git rev-list --count HEAD)
|
||||
|
||||
echo "Branch: $BRANCH"
|
||||
echo "Tag: $GIT_TAG"
|
||||
|
||||
sed -i \
|
||||
-e "s|@GIT_COMMIT_HASH@|$COMMIT_HASH|" \
|
||||
-e "s|@GIT_BRANCH@|$BRANCH|" \
|
||||
-e "s|@GIT_COMMIT_MESSAGE@|$COMMIT_MSG|" \
|
||||
-e "s|@GIT_COMMIT_DATE@|$COMMIT_DATE|" \
|
||||
-e "s|@GIT_DIRTY@|$GIT_DIRTY|" \
|
||||
-e "s|@GIT_TAG@|$GIT_TAG|" \
|
||||
-e "s|@GIT_COMMITS@|$GIT_COMMITS|" \
|
||||
src/version.h.in
|
||||
git fetch --unshallow || echo "failed unshallowing"
|
||||
bash -c scripts/generateVersion.sh
|
||||
|
||||
- name: Create tarball with submodules
|
||||
id: tar
|
||||
|
|
|
|||
139
.github/workflows/translation-ai-check.yml
vendored
139
.github/workflows/translation-ai-check.yml
vendored
|
|
@ -1,139 +0,0 @@
|
|||
name: AI Translation Check
|
||||
|
||||
on:
|
||||
# pull_request_target:
|
||||
# types:
|
||||
# - opened
|
||||
issue_comment:
|
||||
types:
|
||||
- created
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
review:
|
||||
name: Review Translation
|
||||
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
|
||||
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
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -32,8 +32,6 @@ src/render/shaders/*.inc
|
|||
src/render/shaders/Shaders.hpp
|
||||
|
||||
hyprctl/hyprctl
|
||||
hyprctl/hw-protocols/*.c*
|
||||
hyprctl/hw-protocols/*.h*
|
||||
|
||||
gmon.out
|
||||
*.out
|
||||
|
|
|
|||
319
CMakeLists.txt
319
CMakeLists.txt
|
|
@ -17,21 +17,18 @@ set(HYPRLAND_VERSION ${VER})
|
|||
set(PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
set(BINDIR ${CMAKE_INSTALL_BINDIR})
|
||||
configure_file(hyprland.pc.in hyprland.pc @ONLY)
|
||||
|
||||
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
|
||||
|
||||
message(STATUS "Gathering git info")
|
||||
|
||||
# Get git info hash and branch
|
||||
execute_process(COMMAND ./scripts/generateVersion.sh
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
# Make shader files includable
|
||||
execute_process(COMMAND ./scripts/generateShaderIncludes.sh
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
RESULT_VARIABLE HYPR_SHADER_GEN_RESULT)
|
||||
if(NOT HYPR_SHADER_GEN_RESULT EQUAL 0)
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"Failed to generate shader includes (scripts/generateShaderIncludes.sh), exit code: ${HYPR_SHADER_GEN_RESULT}"
|
||||
)
|
||||
endif()
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
|
|
@ -39,23 +36,11 @@ find_package(PkgConfig REQUIRED)
|
|||
# provide a .pc file and won't be detected this way
|
||||
pkg_check_modules(udis_dep IMPORTED_TARGET udis86>=1.7.2)
|
||||
|
||||
# Find non-pkgconfig udis86, otherwise fallback to subproject
|
||||
# Fallback to subproject
|
||||
if(NOT udis_dep_FOUND)
|
||||
find_library(udis_nopc udis86)
|
||||
if(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND"))
|
||||
message(STATUS "Found udis86 at ${udis_nopc}")
|
||||
else()
|
||||
add_subdirectory("subprojects/udis86")
|
||||
include_directories("subprojects/udis86")
|
||||
message(STATUS "udis86 dependency not found, falling back to subproject")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_library(librt rt)
|
||||
if("${librt}" MATCHES "librt-NOTFOUND")
|
||||
unset(LIBRT)
|
||||
else()
|
||||
set(LIBRT rt)
|
||||
add_subdirectory("subprojects/udis86")
|
||||
include_directories("subprojects/udis86")
|
||||
message(STATUS "udis86 dependency not found, falling back to subproject")
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
|
|
@ -86,11 +71,9 @@ message(
|
|||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Configuring Hyprland in Debug with CMake")
|
||||
add_compile_definitions(HYPRLAND_DEBUG)
|
||||
set(BUILD_TESTING ON)
|
||||
else()
|
||||
add_compile_options(-O3)
|
||||
message(STATUS "Configuring Hyprland in Release with CMake")
|
||||
set(BUILD_TESTING OFF)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
|
||||
|
|
@ -112,9 +95,6 @@ add_compile_options(
|
|||
-Wno-clobbered
|
||||
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
|
||||
|
||||
# disable lto as it may break plugins
|
||||
add_compile_options(-fno-lto)
|
||||
|
||||
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
|
|
@ -125,153 +105,48 @@ find_package(Threads REQUIRED)
|
|||
set(GLES_VERSION "GLES3")
|
||||
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
||||
|
||||
set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
|
||||
set(HYPRLANG_MINIMUM_VERSION 0.6.7)
|
||||
set(HYPRCURSOR_MINIMUM_VERSION 0.1.7)
|
||||
set(HYPRUTILS_MINIMUM_VERSION 0.11.0)
|
||||
set(HYPRGRAPHICS_MINIMUM_VERSION 0.1.6)
|
||||
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=${AQUAMARINE_MINIMUM_VERSION})
|
||||
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=${HYPRLANG_MINIMUM_VERSION})
|
||||
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=${HYPRCURSOR_MINIMUM_VERSION})
|
||||
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=${HYPRUTILS_MINIMUM_VERSION})
|
||||
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=${HYPRGRAPHICS_MINIMUM_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(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.3)
|
||||
|
||||
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
|
||||
list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR)
|
||||
list(GET AQ_VERSION_LIST 1 AQ_VERSION_MINOR)
|
||||
list(GET AQ_VERSION_LIST 2 AQ_VERSION_PATCH)
|
||||
|
||||
set(AQUAMARINE_VERSION "${aquamarine_dep_VERSION}")
|
||||
set(AQUAMARINE_VERSION_MAJOR "${AQ_VERSION_MAJOR}")
|
||||
set(AQUAMARINE_VERSION_MINOR "${AQ_VERSION_MINOR}")
|
||||
set(AQUAMARINE_VERSION_PATCH "${AQ_VERSION_PATCH}")
|
||||
set(HYPRLANG_VERSION "${hyprlang_dep_VERSION}")
|
||||
set(HYPRUTILS_VERSION "${hyprutils_dep_VERSION}")
|
||||
set(HYPRCURSOR_VERSION "${hyprcursor_dep_VERSION}")
|
||||
set(HYPRGRAPHICS_VERSION "${hyprgraphics_dep_VERSION}")
|
||||
|
||||
|
||||
find_package(Git QUIET)
|
||||
|
||||
# Populate variables with env vars if present
|
||||
set(GIT_COMMIT_HASH "$ENV{GIT_COMMIT_HASH}")
|
||||
if(NOT GIT_COMMIT_HASH)
|
||||
set(GIT_COMMIT_HASH "unknown")
|
||||
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(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --show-toplevel
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_TOPLEVEL
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE GIT_TOPLEVEL_RESULT
|
||||
)
|
||||
|
||||
if(GIT_TOPLEVEL_RESULT EQUAL 0)
|
||||
message(STATUS "Detected git repository root: ${GIT_TOPLEVEL}")
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
|
||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current
|
||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||
OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
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 --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 --
|
||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||
RESULT_VARIABLE GIT_DIRTY_RESULT)
|
||||
if(NOT GIT_DIRTY_RESULT EQUAL 0)
|
||||
set(GIT_DIRTY "dirty")
|
||||
else()
|
||||
set(GIT_DIRTY "clean")
|
||||
endif()
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags
|
||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||
OUTPUT_VARIABLE GIT_TAG OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-list --count HEAD
|
||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||
OUTPUT_VARIABLE GIT_COMMITS OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
else()
|
||||
message(WARNING "No Git repository detected in ${CMAKE_SOURCE_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/src/version.h.in
|
||||
${CMAKE_SOURCE_DIR}/src/version.h
|
||||
@ONLY
|
||||
)
|
||||
|
||||
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/version.h PROPERTIES GENERATED TRUE)
|
||||
|
||||
set(XKBCOMMON_MINIMUM_VERSION 1.11.0)
|
||||
set(WAYLAND_SERVER_MINIMUM_VERSION 1.22.90)
|
||||
set(WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION 1.45)
|
||||
set(LIBINPUT_MINIMUM_VERSION 1.28)
|
||||
add_compile_definitions(AQUAMARINE_VERSION="${aquamarine_dep_VERSION}")
|
||||
add_compile_definitions(AQUAMARINE_VERSION_MAJOR=${AQ_VERSION_MAJOR})
|
||||
add_compile_definitions(AQUAMARINE_VERSION_MINOR=${AQ_VERSION_MINOR})
|
||||
add_compile_definitions(AQUAMARINE_VERSION_PATCH=${AQ_VERSION_PATCH})
|
||||
add_compile_definitions(HYPRLANG_VERSION="${hyprlang_dep_VERSION}")
|
||||
add_compile_definitions(HYPRUTILS_VERSION="${hyprutils_dep_VERSION}")
|
||||
add_compile_definitions(HYPRCURSOR_VERSION="${hyprcursor_dep_VERSION}")
|
||||
add_compile_definitions(HYPRGRAPHICS_VERSION="${hyprgraphics_dep_VERSION}")
|
||||
|
||||
pkg_check_modules(
|
||||
deps
|
||||
REQUIRED
|
||||
IMPORTED_TARGET GLOBAL
|
||||
xkbcommon>=${XKBCOMMON_MINIMUM_VERSION}
|
||||
IMPORTED_TARGET
|
||||
xkbcommon
|
||||
uuid
|
||||
wayland-server>=${WAYLAND_SERVER_MINIMUM_VERSION}
|
||||
wayland-protocols>=${WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION}
|
||||
wayland-server>=1.22.90
|
||||
wayland-protocols>=1.45
|
||||
cairo
|
||||
pango
|
||||
pangocairo
|
||||
pixman-1
|
||||
xcursor
|
||||
libdrm
|
||||
libinput>=${LIBINPUT_MINIMUM_VERSION}
|
||||
libinput>=1.28
|
||||
gbm
|
||||
gio-2.0
|
||||
re2
|
||||
muparser)
|
||||
re2)
|
||||
|
||||
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES "src/*.cpp")
|
||||
get_filename_component(FULL_MAIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp ABSOLUTE)
|
||||
list(REMOVE_ITEM SRCFILES "${FULL_MAIN_PATH}")
|
||||
|
||||
set(TRACY_CPP_FILES "")
|
||||
if(USE_TRACY)
|
||||
|
|
@ -279,12 +154,7 @@ if(USE_TRACY)
|
|||
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
|
||||
endif()
|
||||
|
||||
add_library(hyprland_lib STATIC ${SRCFILES})
|
||||
add_executable(Hyprland src/main.cpp ${TRACY_CPP_FILES})
|
||||
target_link_libraries(Hyprland hyprland_lib)
|
||||
|
||||
target_include_directories(hyprland_lib PUBLIC ${deps_INCLUDE_DIRS})
|
||||
target_include_directories(Hyprland PUBLIC ${deps_INCLUDE_DIRS})
|
||||
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
|
||||
|
||||
set(USE_GPROF OFF)
|
||||
|
||||
|
|
@ -294,8 +164,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
|||
if(WITH_ASAN)
|
||||
message(STATUS "Enabling ASan")
|
||||
|
||||
target_link_libraries(hyprland_lib PUBLIC asan)
|
||||
target_compile_options(hyprland_lib PUBLIC -fsanitize=address)
|
||||
target_link_libraries(Hyprland asan)
|
||||
target_compile_options(Hyprland PUBLIC -fsanitize=address)
|
||||
endif()
|
||||
|
||||
if(USE_TRACY)
|
||||
|
|
@ -305,7 +175,7 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
|||
option(TRACY_ON_DEMAND "" ON)
|
||||
add_subdirectory(subprojects/tracy)
|
||||
|
||||
target_link_libraries(hyprland_lib PUBLIC Tracy::TracyClient)
|
||||
target_link_libraries(Hyprland Tracy::TracyClient)
|
||||
|
||||
if(USE_TRACY_GPU)
|
||||
message(STATUS "Tracy GPU Profiling is turned on")
|
||||
|
|
@ -321,10 +191,6 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILT_WITH_NIX)
|
||||
add_compile_definitions(BUILT_WITH_NIX)
|
||||
endif()
|
||||
|
||||
check_include_file("execinfo.h" EXECINFOH)
|
||||
if(EXECINFOH)
|
||||
message(STATUS "Configuration supports execinfo")
|
||||
|
|
@ -334,19 +200,19 @@ endif()
|
|||
include(CheckLibraryExists)
|
||||
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
|
||||
if(HAVE_LIBEXECINFO)
|
||||
target_link_libraries(hyprland_lib PUBLIC execinfo)
|
||||
target_link_libraries(Hyprland execinfo)
|
||||
endif()
|
||||
|
||||
check_include_file("sys/timerfd.h" HAS_TIMERFD)
|
||||
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
|
||||
if(NOT HAS_TIMERFD AND epoll_FOUND)
|
||||
target_link_libraries(hyprland_lib PUBLIC PkgConfig::epoll)
|
||||
target_link_libraries(Hyprland PkgConfig::epoll)
|
||||
endif()
|
||||
|
||||
check_include_file("sys/inotify.h" HAS_INOTIFY)
|
||||
pkg_check_modules(inotify IMPORTED_TARGET libinotify)
|
||||
if(NOT HAS_INOTIFY AND inotify_FOUND)
|
||||
target_link_libraries(hyprland_lib PUBLIC PkgConfig::inotify)
|
||||
target_link_libraries(Hyprland PkgConfig::inotify)
|
||||
endif()
|
||||
|
||||
if(NO_XWAYLAND)
|
||||
|
|
@ -354,7 +220,10 @@ if(NO_XWAYLAND)
|
|||
add_compile_definitions(NO_XWAYLAND)
|
||||
else()
|
||||
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
|
||||
set(XWAYLAND_DEPENDENCIES
|
||||
pkg_check_modules(
|
||||
xdeps
|
||||
REQUIRED
|
||||
IMPORTED_TARGET
|
||||
xcb
|
||||
xcb-render
|
||||
xcb-xfixes
|
||||
|
|
@ -362,21 +231,9 @@ else()
|
|||
xcb-composite
|
||||
xcb-res
|
||||
xcb-errors)
|
||||
|
||||
pkg_check_modules(
|
||||
xdeps
|
||||
REQUIRED
|
||||
IMPORTED_TARGET
|
||||
${XWAYLAND_DEPENDENCIES})
|
||||
|
||||
string(JOIN ", " PKGCONFIG_XWAYLAND_DEPENDENCIES ${XWAYLAND_DEPENDENCIES})
|
||||
string(PREPEND PKGCONFIG_XWAYLAND_DEPENDENCIES ", ")
|
||||
|
||||
target_link_libraries(hyprland_lib PUBLIC PkgConfig::xdeps)
|
||||
target_link_libraries(Hyprland PkgConfig::xdeps)
|
||||
endif()
|
||||
|
||||
configure_file(hyprland.pc.in hyprland.pc @ONLY)
|
||||
|
||||
if(NO_SYSTEMD)
|
||||
message(STATUS "SYSTEMD support is disabled...")
|
||||
else()
|
||||
|
|
@ -397,42 +254,30 @@ set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
|||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
include(CPack)
|
||||
|
||||
if(CMAKE_DISABLE_PRECOMPILE_HEADERS)
|
||||
message(STATUS "Not using precompiled headers")
|
||||
else()
|
||||
message(STATUS "Setting precompiled headers")
|
||||
target_precompile_headers(hyprland_lib PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:src/pch/pch.hpp>)
|
||||
endif()
|
||||
message(STATUS "Setting precompiled headers")
|
||||
|
||||
target_precompile_headers(Hyprland PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:src/pch/pch.hpp>)
|
||||
|
||||
message(STATUS "Setting link libraries")
|
||||
|
||||
target_link_libraries(
|
||||
hyprland_lib
|
||||
PUBLIC
|
||||
Hyprland
|
||||
rt
|
||||
PkgConfig::aquamarine_dep
|
||||
PkgConfig::hyprlang_dep
|
||||
PkgConfig::hyprutils_dep
|
||||
PkgConfig::hyprcursor_dep
|
||||
PkgConfig::hyprgraphics_dep
|
||||
PkgConfig::deps
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
Hyprland
|
||||
${LIBRT}
|
||||
hyprland_lib)
|
||||
PkgConfig::deps)
|
||||
if(udis_dep_FOUND)
|
||||
target_link_libraries(hyprland_lib PUBLIC PkgConfig::udis_dep)
|
||||
elseif(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND"))
|
||||
target_link_libraries(hyprland_lib PUBLIC ${udis_nopc})
|
||||
target_link_libraries(Hyprland PkgConfig::udis_dep)
|
||||
else()
|
||||
target_link_libraries(hyprland_lib PUBLIC libudis86)
|
||||
target_link_libraries(Hyprland libudis86)
|
||||
endif()
|
||||
|
||||
# used by `make installheaders`, to ensure the headers are generated
|
||||
add_custom_target(generate-protocol-headers)
|
||||
set(PROTOCOL_SOURCES "")
|
||||
|
||||
function(protocolnew protoPath protoName external)
|
||||
if(external)
|
||||
|
|
@ -446,15 +291,10 @@ function(protocolnew protoPath protoName external)
|
|||
COMMAND hyprwayland-scanner ${path}/${protoName}.xml
|
||||
${CMAKE_SOURCE_DIR}/protocols/
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
target_sources(hyprland_lib PRIVATE protocols/${protoName}.cpp
|
||||
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp
|
||||
protocols/${protoName}.hpp)
|
||||
target_sources(generate-protocol-headers
|
||||
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp)
|
||||
|
||||
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp")
|
||||
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp")
|
||||
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
function(protocolWayland)
|
||||
add_custom_command(
|
||||
|
|
@ -464,17 +304,12 @@ function(protocolWayland)
|
|||
hyprwayland-scanner --wayland-enums
|
||||
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
target_sources(hyprland_lib PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
|
||||
target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
|
||||
target_sources(generate-protocol-headers
|
||||
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
|
||||
|
||||
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.hpp")
|
||||
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.cpp")
|
||||
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL Threads::Threads)
|
||||
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
|
||||
|
||||
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
|
||||
if(hyprland_protocols_dep_FOUND)
|
||||
|
|
@ -549,14 +384,11 @@ protocolnew("staging/xdg-system-bell" "xdg-system-bell-v1" false)
|
|||
protocolnew("staging/ext-workspace" "ext-workspace-v1" false)
|
||||
protocolnew("staging/ext-data-control" "ext-data-control-v1" false)
|
||||
protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
|
||||
protocolnew("staging/fifo" "fifo-v1" false)
|
||||
protocolnew("staging/commit-timing" "commit-timing-v1" false)
|
||||
|
||||
protocolwayland()
|
||||
|
||||
# tools
|
||||
add_subdirectory(hyprctl)
|
||||
add_subdirectory(start)
|
||||
|
||||
if(NO_HYPRPM)
|
||||
message(STATUS "hyprpm is disabled")
|
||||
|
|
@ -583,6 +415,7 @@ 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)
|
||||
|
||||
|
|
@ -620,47 +453,19 @@ install(
|
|||
PATTERN "*.hpp"
|
||||
PATTERN "*.inc")
|
||||
|
||||
if(BUILD_TESTING OR WITH_TESTS)
|
||||
message(STATUS "Building tests")
|
||||
|
||||
# hyprtester
|
||||
add_subdirectory(hyprtester)
|
||||
|
||||
# GTest
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
include(GoogleTest)
|
||||
file(GLOB_RECURSE TESTFILES "tests/*.cpp")
|
||||
add_executable(hyprland_gtests ${TESTFILES})
|
||||
target_compile_options(hyprland_gtests PRIVATE --coverage)
|
||||
target_link_options(hyprland_gtests PRIVATE --coverage)
|
||||
target_include_directories(
|
||||
hyprland_gtests
|
||||
PUBLIC "./include"
|
||||
PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
|
||||
|
||||
target_link_libraries(hyprland_gtests hyprland_lib GTest::gtest_main)
|
||||
|
||||
gtest_discover_tests(hyprland_gtests)
|
||||
|
||||
# Enable coverage in main hyprland lib
|
||||
target_compile_options(hyprland_lib PRIVATE --coverage)
|
||||
target_link_options(hyprland_lib PRIVATE --coverage)
|
||||
target_link_libraries(hyprland_lib PUBLIC gcov)
|
||||
|
||||
# Enable coverage in hyprland exe
|
||||
target_compile_options(Hyprland PRIVATE --coverage)
|
||||
target_link_options(Hyprland PRIVATE --coverage)
|
||||
target_link_libraries(Hyprland gcov)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
message(STATUS "Testing is enabled")
|
||||
if(TESTS)
|
||||
message(STATUS "building tests is enabled TESTS")
|
||||
|
||||
enable_testing()
|
||||
add_custom_target(tests)
|
||||
|
||||
add_dependencies(tests hyprland_gtests)
|
||||
add_subdirectory(hyprtester)
|
||||
add_test(
|
||||
NAME "Main Test"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hyprtester
|
||||
COMMAND hyprtester)
|
||||
|
||||
add_dependencies(tests hyprtester)
|
||||
else()
|
||||
message(STATUS "Testing is disabled")
|
||||
message(STATUS "building tests is disabled")
|
||||
endif()
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -8,7 +8,7 @@ release:
|
|||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
debug:
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DTESTS=true -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
|
||||
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
nopch:
|
||||
|
|
|
|||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.52.0
|
||||
0.51.0
|
||||
|
|
|
|||
10
assets/install/meson.build
Normal file
10
assets/install/meson.build
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
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
|
||||
7
assets/meson.build
Normal file
7
assets/meson.build
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
install_data(
|
||||
'hyprland-portals.conf',
|
||||
install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'),
|
||||
install_tag: 'runtime',
|
||||
)
|
||||
|
||||
subdir('install')
|
||||
2
docs/meson.build
Normal file
2
docs/meson.build
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
install_man('Hyprland.1')
|
||||
install_man('hyprctl.1')
|
||||
|
|
@ -27,7 +27,7 @@ monitor=,preferred,auto,auto
|
|||
# Set programs that you use
|
||||
$terminal = kitty
|
||||
$fileManager = dolphin
|
||||
$menu = hyprlauncher
|
||||
$menu = wofi --show drun
|
||||
|
||||
|
||||
#################
|
||||
|
|
@ -159,23 +159,10 @@ animations {
|
|||
# uncomment all if you wish to use that.
|
||||
# workspace = w[tv1], gapsout:0, gapsin:0
|
||||
# workspace = f[1], gapsout:0, gapsin:0
|
||||
# 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
|
||||
# }
|
||||
# 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]
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
|
|
@ -237,7 +224,7 @@ $mainMod = SUPER # Sets "Windows" key as main modifier
|
|||
# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
|
||||
bind = $mainMod, Q, exec, $terminal
|
||||
bind = $mainMod, C, killactive,
|
||||
bind = $mainMod, M, exec, command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit
|
||||
bind = $mainMod, M, exit,
|
||||
bind = $mainMod, E, exec, $fileManager
|
||||
bind = $mainMod, V, togglefloating,
|
||||
bind = $mainMod, R, exec, $menu
|
||||
|
|
@ -307,35 +294,11 @@ 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 windowrules that are useful
|
||||
# Example windowrule
|
||||
# windowrule = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
windowrule {
|
||||
# Ignore maximize requests from all apps. You'll probably like this.
|
||||
name = suppress-maximize-events
|
||||
match:class = .*
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrule = suppressevent maximize, class:.*
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Hyprland-run windowrule
|
||||
windowrule {
|
||||
name = move-hyprland-run
|
||||
|
||||
match:class = hyprland-run
|
||||
|
||||
move = 20 monitor_h-120
|
||||
float = yes
|
||||
}
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[Desktop Entry]
|
||||
Name=Hyprland
|
||||
Comment=An intelligent dynamic tiling Wayland compositor
|
||||
Exec=start-hyprland
|
||||
Exec=Hyprland
|
||||
Type=Application
|
||||
DesktopNames=Hyprland
|
||||
Keywords=tiling;wayland;compositor;
|
||||
|
|
|
|||
10
example/meson.build
Normal file
10
example/meson.build
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
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',
|
||||
)
|
||||
234
flake.lock
generated
234
flake.lock
generated
|
|
@ -16,11 +16,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765900596,
|
||||
"narHash": "sha256-+hn8v9jkkLP9m+o0Nm5SiEq10W0iWDSotH2XfjU45fA=",
|
||||
"lastModified": 1755946532,
|
||||
"narHash": "sha256-POePremlUY5GyA1zfbtic6XLxDaQcqHN6l+bIxdT5gc=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"rev": "d83c97f8f5c0aae553c1489c7d9eff3eadcadace",
|
||||
"rev": "81584dae2df6ac79f6b6dae0ecb7705e95129ada",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -32,11 +32,11 @@
|
|||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1761588595,
|
||||
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -105,11 +105,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763733840,
|
||||
"narHash": "sha256-JnET78yl5RvpGuDQy3rCycOCkiKoLr5DN1fPhRNNMco=",
|
||||
"lastModified": 1756891319,
|
||||
"narHash": "sha256-/e6OXxzbAj/o97Z1dZgHre4bNaVjapDGscAujSCQSbI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "8f1bec691b2d198c60cccabca7a94add2df4ed1a",
|
||||
"rev": "621e2e00f1736aa18c68f7dfbf2b9cff94b8cc4d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -118,45 +118,6 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-guiutils": {
|
||||
"inputs": {
|
||||
"aquamarine": [
|
||||
"aquamarine"
|
||||
],
|
||||
"hyprgraphics": [
|
||||
"hyprgraphics"
|
||||
],
|
||||
"hyprlang": [
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprtoolkit": "hyprtoolkit",
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765643131,
|
||||
"narHash": "sha256-CCGohW5EBIRy4B7vTyBMqPgsNcaNenVad/wszfddET0=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-guiutils",
|
||||
"rev": "e50ae912813bdfa8372d62daf454f48d6df02297",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-guiutils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-protocols": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
|
@ -167,11 +128,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765214753,
|
||||
"narHash": "sha256-P9zdGXOzToJJgu5sVjv7oeOGPIIwrd9hAUAP3PsmBBs=",
|
||||
"lastModified": 1749046714,
|
||||
"narHash": "sha256-kymV5FMnddYGI+UjwIw8ceDjdeg7ToDVjbHCvUlhn14=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "3f3860b869014c00e8b9e0528c7b4ddc335c21ab",
|
||||
"rev": "613878cb6f459c5e323aaafe1e6f388ac8a36330",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -180,6 +141,67 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qt-support": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
"hyprland-qtutils",
|
||||
"hyprlang"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland-qtutils",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland-qtutils",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749154592,
|
||||
"narHash": "sha256-DO7z5CeT/ddSGDEnK9mAXm1qlGL47L3VAHLlLXoCjhE=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"rev": "4c8053c3c888138a30c3a6c45c2e45f5484f2074",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qtutils": {
|
||||
"inputs": {
|
||||
"hyprland-qt-support": "hyprland-qt-support",
|
||||
"hyprlang": [
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"hyprland-qtutils",
|
||||
"hyprlang",
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753819801,
|
||||
"narHash": "sha256-tHe6XeNeVeKapkNM3tcjW4RuD+tB2iwwoogWJOtsqTI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"rev": "b308a818b9dcaa7ab8ccab891c1b84ebde2152bc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qtutils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprlang": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
|
|
@ -193,11 +215,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1764612430,
|
||||
"narHash": "sha256-54ltTSbI6W+qYGMchAgCR6QnC1kOdKXN6X6pJhOWxFg=",
|
||||
"lastModified": 1756810301,
|
||||
"narHash": "sha256-wgZ3VW4VVtjK5dr0EiK9zKdJ/SOqGIBXVG85C3LVxQA=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "0d00dc118981531aa731150b6ea551ef037acddd",
|
||||
"rev": "3d63fb4a42c819f198deabd18c0c2c1ded1de931",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -206,51 +228,6 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprtoolkit": {
|
||||
"inputs": {
|
||||
"aquamarine": [
|
||||
"hyprland-guiutils",
|
||||
"aquamarine"
|
||||
],
|
||||
"hyprgraphics": [
|
||||
"hyprland-guiutils",
|
||||
"hyprgraphics"
|
||||
],
|
||||
"hyprlang": [
|
||||
"hyprland-guiutils",
|
||||
"hyprlang"
|
||||
],
|
||||
"hyprutils": [
|
||||
"hyprland-guiutils",
|
||||
"hyprutils"
|
||||
],
|
||||
"hyprwayland-scanner": [
|
||||
"hyprland-guiutils",
|
||||
"hyprwayland-scanner"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland-guiutils",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland-guiutils",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1764592794,
|
||||
"narHash": "sha256-7CcO+wbTJ1L1NBQHierHzheQGPWwkIQug/w+fhTAVuU=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprtoolkit",
|
||||
"rev": "5cfe0743f0e608e1462972303778d8a0859ee63e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprtoolkit",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprutils": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
|
|
@ -261,11 +238,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766160771,
|
||||
"narHash": "sha256-roINUGikWRqqgKrD4iotKbGj3ZKJl3hjMz5l/SyKrHw=",
|
||||
"lastModified": 1756117388,
|
||||
"narHash": "sha256-oRDel6pNl/T2tI+nc/USU9ZP9w08dxtl7hiZxa0C/Wc=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "5ac060bfcf2f12b3a6381156ebbc13826a05b09f",
|
||||
"rev": "b2ae3204845f5f2f79b4703b441252d8ad2ecfd0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -284,11 +261,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763640274,
|
||||
"narHash": "sha256-Uan1Nl9i4TF/kyFoHnTq1bd/rsWh4GAK/9/jDqLbY5A=",
|
||||
"lastModified": 1755184602,
|
||||
"narHash": "sha256-RCBQN8xuADB0LEgaKbfRqwm6CdyopE1xIEhNc67FAbw=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "f6cf414ca0e16a4d30198fd670ec86df3c89f671",
|
||||
"rev": "b3b0f1f40ae09d4447c20608e5a4faf8bf3c492d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -297,39 +274,13 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprwire": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766253200,
|
||||
"narHash": "sha256-26qPwrd3od+xoYVywSB7hC2cz9ivN46VPLlrsXyGxvE=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwire",
|
||||
"rev": "1079777525b30a947c8d657fac158e00ae85de9d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwire",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1766070988,
|
||||
"narHash": "sha256-G/WVghka6c4bAzMhTwT2vjLccg/awmHkdKSd2JrycLc=",
|
||||
"lastModified": 1757068644,
|
||||
"narHash": "sha256-NOrUtIhTkIIumj1E/Rsv1J37Yi3xGStISEo8tZm3KW4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c6245e83d836d0433170a16eb185cefe0572f8b8",
|
||||
"rev": "8eb28adfa3dc4de28e792e3bf49fcf9007ca8ac9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -348,11 +299,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765911976,
|
||||
"narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=",
|
||||
"lastModified": 1757239681,
|
||||
"narHash": "sha256-E9spYi9lxm2f1zWQLQ7xQt8Xs2nWgr1T4QM7ZjLFphM=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "b68b780b69702a090c8bb1b973bab13756cc7a27",
|
||||
"rev": "ab82ab08d6bf74085bd328de2a8722c12d97bd9d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -366,12 +317,11 @@
|
|||
"aquamarine": "aquamarine",
|
||||
"hyprcursor": "hyprcursor",
|
||||
"hyprgraphics": "hyprgraphics",
|
||||
"hyprland-guiutils": "hyprland-guiutils",
|
||||
"hyprland-protocols": "hyprland-protocols",
|
||||
"hyprland-qtutils": "hyprland-qtutils",
|
||||
"hyprlang": "hyprlang",
|
||||
"hyprutils": "hyprutils",
|
||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||
"hyprwire": "hyprwire",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks",
|
||||
"systems": "systems",
|
||||
|
|
@ -415,11 +365,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761431178,
|
||||
"narHash": "sha256-xzjC1CV3+wpUQKNF+GnadnkeGUCJX+vgaWIZsnz9tzI=",
|
||||
"lastModified": 1755354946,
|
||||
"narHash": "sha256-zdov5f/GcoLQc9qYIS1dUTqtJMeDqmBmo59PAxze6e4=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"rev": "4b8801228ff958d028f588f0c2b911dbf32297f9",
|
||||
"rev": "a10726d6a8d0ef1a0c645378f983b6278c42eaa0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
17
flake.nix
17
flake.nix
|
|
@ -35,15 +35,11 @@
|
|||
inputs.systems.follows = "systems";
|
||||
};
|
||||
|
||||
hyprland-guiutils = {
|
||||
url = "github:hyprwm/hyprland-guiutils";
|
||||
hyprland-qtutils = {
|
||||
url = "github:hyprwm/hyprland-qtutils";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.aquamarine.follows = "aquamarine";
|
||||
inputs.hyprgraphics.follows = "hyprgraphics";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
inputs.hyprlang.follows = "hyprlang";
|
||||
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
|
||||
};
|
||||
|
||||
hyprlang = {
|
||||
|
|
@ -65,13 +61,6 @@
|
|||
inputs.systems.follows = "systems";
|
||||
};
|
||||
|
||||
hyprwire = {
|
||||
url = "github:hyprwm/hyprwire";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
};
|
||||
|
||||
xdph = {
|
||||
url = "github:hyprwm/xdg-desktop-portal-hyprland";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
|
|
@ -159,7 +148,7 @@
|
|||
# hyprland-packages
|
||||
hyprland
|
||||
hyprland-unwrapped
|
||||
hyprland-with-tests
|
||||
hyprtester
|
||||
# hyprland-extras
|
||||
xdg-desktop-portal-hyprland
|
||||
;
|
||||
|
|
|
|||
|
|
@ -5,32 +5,11 @@ project(
|
|||
DESCRIPTION "Control utility for Hyprland"
|
||||
)
|
||||
|
||||
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 hyprwire re2)
|
||||
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 re2)
|
||||
|
||||
file(GLOB_RECURSE HYPRCTL_SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "hw-protocols/*.cpp" "include/*.hpp")
|
||||
|
||||
add_executable(hyprctl ${HYPRCTL_SRCFILES})
|
||||
add_executable(hyprctl "main.cpp")
|
||||
|
||||
target_link_libraries(hyprctl PUBLIC PkgConfig::hyprctl_deps)
|
||||
target_include_directories(hyprctl PRIVATE "hw-protocols")
|
||||
|
||||
# Hyprwire
|
||||
|
||||
function(hyprprotocol protoPath protoName)
|
||||
set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath})
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-spec.hpp
|
||||
COMMAND hyprwire-scanner --client ${path}/${protoName}.xml
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_sources(hyprctl PRIVATE hw-protocols/${protoName}-client.cpp
|
||||
hw-protocols/${protoName}-client.hpp
|
||||
hw-protocols/${protoName}-spec.hpp)
|
||||
endfunction()
|
||||
|
||||
hyprprotocol(hw-protocols hyprpaper_core)
|
||||
|
||||
# binary
|
||||
install(TARGETS hyprctl)
|
||||
|
|
|
|||
|
|
@ -74,8 +74,11 @@ flags:
|
|||
const std::string_view HYPRPAPER_HELP = R"#(usage: hyprctl [flags] hyprpaper <request>
|
||||
|
||||
requests:
|
||||
wallpaper → Issue a wallpaper to call a config wallpaper dynamically.
|
||||
Arguments are [mon],[path],[fit_mode]. Fit mode is optional.
|
||||
listactive → Lists all active images
|
||||
listloaded → Lists all loaded images
|
||||
preload <path> → Preloads image
|
||||
unload <path> → Unloads image. Pass 'all' as path to unload all images
|
||||
wallpaper → Issue a wallpaper to call a config wallpaper dynamically
|
||||
|
||||
flags:
|
||||
See 'hyprctl --help')#";
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="hyprpaper_core" version="1">
|
||||
<copyright>
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2025, Hypr Development
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
</copyright>
|
||||
|
||||
<object name="hyprpaper_core_manager" version="1">
|
||||
<description summary="manager object">
|
||||
This is the core manager object for hyprpaper operations
|
||||
</description>
|
||||
|
||||
<c2s name="get_wallpaper_object">
|
||||
<description summary="Get a wallpaper object">
|
||||
Creates a wallpaper object
|
||||
</description>
|
||||
<returns iface="hyprpaper_wallpaper"/>
|
||||
</c2s>
|
||||
|
||||
<s2c name="add_monitor">
|
||||
<description summary="New monitor added">
|
||||
Emitted when a new monitor is added.
|
||||
</description>
|
||||
<arg name="monitor_name" type="varchar" summary="the monitor's name"/>
|
||||
</s2c>
|
||||
|
||||
<s2c name="remove_monitor">
|
||||
<description summary="A monitor was removed">
|
||||
Emitted when a monitor is removed.
|
||||
</description>
|
||||
<arg name="monitor_name" type="varchar" summary="the monitor's name"/>
|
||||
</s2c>
|
||||
|
||||
<c2s name="destroy" destructor="true">
|
||||
<description summary="Destroy this object">
|
||||
Destroys this object. Children remain alive until destroyed.
|
||||
</description>
|
||||
</c2s>
|
||||
</object>
|
||||
|
||||
<enum name="wallpaper_fit_mode">
|
||||
<value idx="0" name="stretch"/>
|
||||
<value idx="1" name="cover"/>
|
||||
<value idx="2" name="contain"/>
|
||||
<value idx="3" name="tile"/>
|
||||
</enum>
|
||||
|
||||
<enum name="wallpaper_errors">
|
||||
<value idx="0" name="inert_wallpaper_object" description="attempted to use an inert wallpaper object"/>
|
||||
</enum>
|
||||
|
||||
<enum name="applying_error">
|
||||
<value idx="0" name="invalid_path" description="path provided was invalid"/>
|
||||
<value idx="1" name="invalid_monitor" description="monitor provided was invalid"/>
|
||||
<value idx="2" name="unknown_error" description="unknown error"/>
|
||||
</enum>
|
||||
|
||||
<object name="hyprpaper_wallpaper" version="1">
|
||||
<description summary="wallpaper object">
|
||||
This is an object describing a wallpaper
|
||||
</description>
|
||||
|
||||
<c2s name="path">
|
||||
<description summary="Set a path">
|
||||
Set a file path for the wallpaper. This has to be an absolute path from the fs root.
|
||||
This is required.
|
||||
</description>
|
||||
<arg name="wallpaper" type="varchar" summary="path"/>
|
||||
</c2s>
|
||||
|
||||
<c2s name="fit_mode">
|
||||
<description summary="Set a fit mode">
|
||||
Set a fit mode for the wallpaper. This is set to cover by default.
|
||||
</description>
|
||||
<arg name="fit_mode" type="enum" interface="wallpaper_fit_mode" summary="path"/>
|
||||
</c2s>
|
||||
|
||||
<c2s name="monitor_name">
|
||||
<description summary="Set the monitor name">
|
||||
Set a monitor for the wallpaper. Setting this to empty (or not setting at all) will
|
||||
treat this as a wildcard fallback.
|
||||
|
||||
See hyprpaper_core_manager.add_monitor and hyprpaper_core_manager.remove_monitor
|
||||
for tracking monitor names.
|
||||
</description>
|
||||
<arg name="monitor_name" type="varchar" summary="monitor name"/>
|
||||
</c2s>
|
||||
|
||||
<c2s name="apply">
|
||||
<description summary="Apply this wallpaper">
|
||||
Applies this object's state to the wallpaper state. Will emit .success on success,
|
||||
and .failed on failure.
|
||||
|
||||
This object becomes inert after .succeess or .failed, the only valid operation
|
||||
is to destroy it afterwards.
|
||||
</description>
|
||||
</c2s>
|
||||
|
||||
<s2c name="success">
|
||||
<description summary="Operation succeeded">
|
||||
Wallpaper was applied successfully.
|
||||
</description>
|
||||
</s2c>
|
||||
|
||||
<s2c name="failed">
|
||||
<description summary="Operation failed">
|
||||
Wallpaper was not applied. See the error field for more information.
|
||||
</description>
|
||||
<arg name="error" type="enum" interface="hyprpaper_wallpaper_application_error" summary="path"/>
|
||||
</s2c>
|
||||
|
||||
<c2s name="destroy" destructor="true">
|
||||
<description summary="Destroy this object">
|
||||
Destroys this object.
|
||||
</description>
|
||||
</c2s>
|
||||
</object>
|
||||
</protocol>
|
||||
|
|
@ -31,7 +31,6 @@ using namespace Hyprutils::String;
|
|||
using namespace Hyprutils::Memory;
|
||||
|
||||
#include "Strings.hpp"
|
||||
#include "hyprpaper/Hyprpaper.hpp"
|
||||
|
||||
std::string instanceSignature;
|
||||
bool quiet = false;
|
||||
|
|
@ -306,6 +305,10 @@ int requestIPC(std::string_view filename, std::string_view arg) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int requestHyprpaper(std::string_view arg) {
|
||||
return requestIPC(".hyprpaper.sock", arg);
|
||||
}
|
||||
|
||||
int requestHyprsunset(std::string_view arg) {
|
||||
return requestIPC(".hyprsunset.sock", arg);
|
||||
}
|
||||
|
|
@ -497,12 +500,9 @@ int main(int argc, char** argv) {
|
|||
|
||||
if (fullRequest.contains("/--batch"))
|
||||
batchRequest(fullRequest, json);
|
||||
else if (fullRequest.contains("/hyprpaper")) {
|
||||
auto result = Hyprpaper::makeHyprpaperRequest(fullRequest);
|
||||
if (!result)
|
||||
log(std::format("error: {}", result.error()));
|
||||
exitStatus = !result;
|
||||
} else if (fullRequest.contains("/hyprsunset"))
|
||||
else if (fullRequest.contains("/hyprpaper"))
|
||||
exitStatus = requestHyprpaper(fullRequest);
|
||||
else if (fullRequest.contains("/hyprsunset"))
|
||||
exitStatus = requestHyprsunset(fullRequest);
|
||||
else if (fullRequest.contains("/switchxkblayout"))
|
||||
exitStatus = request(fullRequest, 2);
|
||||
27
hyprctl/meson.build
Normal file
27
hyprctl/meson.build
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
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',
|
||||
)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include <hyprutils/memory/Atomic.hpp>
|
||||
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
#define UP CUniquePointer
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
#include "Hyprpaper.hpp"
|
||||
#include "../helpers/Memory.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <format>
|
||||
#include <filesystem>
|
||||
|
||||
#include <hyprpaper_core-client.hpp>
|
||||
|
||||
#include <hyprutils/string/VarList2.hpp>
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
constexpr const char* SOCKET_NAME = ".hyprpaper.sock";
|
||||
static SP<CCHyprpaperCoreImpl> g_coreImpl;
|
||||
|
||||
constexpr const uint32_t PROTOCOL_VERSION_SUPPORTED = 1;
|
||||
|
||||
//
|
||||
static hyprpaperCoreWallpaperFitMode fitFromString(const std::string_view& sv) {
|
||||
if (sv == "contain")
|
||||
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_CONTAIN;
|
||||
if (sv == "fit" || sv == "stretch")
|
||||
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_STRETCH;
|
||||
if (sv == "tile")
|
||||
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_TILE;
|
||||
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_COVER;
|
||||
}
|
||||
|
||||
static std::expected<std::string, std::string> resolvePath(const std::string_view& sv) {
|
||||
std::error_code ec;
|
||||
auto can = std::filesystem::canonical(sv, ec);
|
||||
|
||||
if (ec)
|
||||
return std::unexpected(std::format("invalid path: {}", ec.message()));
|
||||
|
||||
return can;
|
||||
}
|
||||
|
||||
static std::expected<std::string, std::string> getFullPath(const std::string_view& sv) {
|
||||
if (sv.empty())
|
||||
return std::unexpected("empty path");
|
||||
|
||||
if (sv[0] == '~') {
|
||||
static auto HOME = getenv("HOME");
|
||||
if (!HOME || HOME[0] == '\0')
|
||||
return std::unexpected("home path but no $HOME");
|
||||
|
||||
return resolvePath(std::string{HOME} + "/"s + std::string{sv.substr(1)});
|
||||
}
|
||||
|
||||
return resolvePath(sv);
|
||||
}
|
||||
|
||||
std::expected<void, std::string> Hyprpaper::makeHyprpaperRequest(const std::string_view& rq) {
|
||||
if (!rq.contains(' '))
|
||||
return std::unexpected("Invalid request");
|
||||
|
||||
if (!rq.starts_with("/hyprpaper "))
|
||||
return std::unexpected("Invalid request");
|
||||
|
||||
std::string_view LHS, RHS;
|
||||
auto spacePos = rq.find(' ', 12);
|
||||
LHS = rq.substr(11, spacePos - 11);
|
||||
RHS = rq.substr(spacePos + 1);
|
||||
|
||||
if (LHS != "wallpaper")
|
||||
return std::unexpected("Unknown hyprpaper request");
|
||||
|
||||
CVarList2 args(std::string{RHS}, 0, ',');
|
||||
|
||||
const std::string MONITOR = std::string{args[0]};
|
||||
const auto& PATH_RAW = args[1];
|
||||
const auto& FIT = args[2];
|
||||
|
||||
if (PATH_RAW.empty())
|
||||
return std::unexpected("not enough args");
|
||||
|
||||
const auto RTDIR = getenv("XDG_RUNTIME_DIR");
|
||||
|
||||
if (!RTDIR || RTDIR[0] == '\0')
|
||||
return std::unexpected("can't send: no XDG_RUNTIME_DIR");
|
||||
|
||||
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (!HIS || HIS[0] == '\0')
|
||||
return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)");
|
||||
|
||||
const auto PATH = getFullPath(PATH_RAW);
|
||||
|
||||
if (!PATH)
|
||||
return std::unexpected(std::format("bad path: {}", PATH_RAW));
|
||||
|
||||
auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME;
|
||||
|
||||
auto socket = Hyprwire::IClientSocket::open(socketPath);
|
||||
|
||||
if (!socket)
|
||||
return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)");
|
||||
|
||||
g_coreImpl = makeShared<CCHyprpaperCoreImpl>(1);
|
||||
|
||||
socket->addImplementation(g_coreImpl);
|
||||
|
||||
if (!socket->waitForHandshake())
|
||||
return std::unexpected("can't send: wire handshake failed");
|
||||
|
||||
auto spec = socket->getSpec(g_coreImpl->protocol()->specName());
|
||||
|
||||
if (!spec)
|
||||
return std::unexpected("can't send: hyprpaper doesn't have the spec?!");
|
||||
|
||||
auto manager = makeShared<CCHyprpaperCoreManagerObject>(socket->bindProtocol(g_coreImpl->protocol(), PROTOCOL_VERSION_SUPPORTED));
|
||||
|
||||
if (!manager)
|
||||
return std::unexpected("wire error: couldn't create manager");
|
||||
|
||||
auto wallpaper = makeShared<CCHyprpaperWallpaperObject>(manager->sendGetWallpaperObject());
|
||||
|
||||
if (!wallpaper)
|
||||
return std::unexpected("wire error: couldn't create wallpaper object");
|
||||
|
||||
bool canExit = false;
|
||||
std::optional<std::string> err;
|
||||
|
||||
wallpaper->setFailed([&canExit, &err](uint32_t code) {
|
||||
canExit = true;
|
||||
err = std::format("failed to set wallpaper, code {}", code);
|
||||
});
|
||||
wallpaper->setSuccess([&canExit]() { canExit = true; });
|
||||
|
||||
wallpaper->sendPath(PATH->c_str());
|
||||
wallpaper->sendMonitorName(MONITOR.c_str());
|
||||
if (!FIT.empty())
|
||||
wallpaper->sendFitMode(fitFromString(FIT));
|
||||
|
||||
wallpaper->sendApply();
|
||||
|
||||
while (!canExit) {
|
||||
socket->dispatchEvents(true);
|
||||
}
|
||||
|
||||
if (err)
|
||||
return std::unexpected(*err);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include <string>
|
||||
|
||||
namespace Hyprpaper {
|
||||
std::expected<void, std::string> makeHyprpaperRequest(const std::string_view& rq);
|
||||
};
|
||||
|
|
@ -4,5 +4,4 @@ Name: Hyprland
|
|||
URL: https://github.com/hyprwm/Hyprland
|
||||
Description: Hyprland header files
|
||||
Version: @HYPRLAND_VERSION@
|
||||
Requires: aquamarine >= @AQUAMARINE_MINIMUM_VERSION@, hyprcursor >= @HYPRCURSOR_MINIMUM_VERSION@, hyprgraphics >= @HYPRGRAPHICS_MINIMUM_VERSION@, hyprlang >= @HYPRLANG_MINIMUM_VERSION@, hyprutils >= @HYPRUTILS_MINIMUM_VERSION@, libdrm, egl, cairo, xkbcommon >= @XKBCOMMON_MINIMUM_VERSION@, libinput >= @LIBINPUT_MINIMUM_VERSION@, wayland-server >= @WAYLAND_SERVER_MINIMUM_VERSION@@PKGCONFIG_XWAYLAND_DEPENDENCIES@
|
||||
Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ set(CMAKE_CXX_STANDARD 23)
|
|||
|
||||
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
|
||||
|
||||
find_package(glaze 6.0.0 QUIET)
|
||||
find_package(glaze QUIET)
|
||||
if (NOT glaze_FOUND)
|
||||
set(GLAZE_VERSION v6.1.0)
|
||||
set(GLAZE_VERSION v5.1.1)
|
||||
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@ static std::string getTempRoot() {
|
|||
|
||||
const auto STR = ENV + std::string{"/hyprpm/"};
|
||||
|
||||
if (!std::filesystem::exists(STR))
|
||||
mkdir(STR.c_str(), S_IRWXU);
|
||||
|
||||
return STR;
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +90,6 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
|||
auto DATA = toml::table{
|
||||
{"repository", toml::table{
|
||||
{"name", repo.name},
|
||||
{"author", repo.author},
|
||||
{"hash", repo.hash},
|
||||
{"url", repo.url},
|
||||
{"rev", repo.rev}
|
||||
|
|
@ -123,32 +119,31 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
|||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
}
|
||||
|
||||
bool DataState::pluginRepoExists(const SPluginRepoIdentifier identifier) {
|
||||
bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto AUTHOR = STATE["repository"]["author"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
|
||||
if (identifier.matches(URL, NAME, AUTHOR))
|
||||
if (URL == urlOrName || NAME == urlOrName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) {
|
||||
void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto AUTHOR = STATE["repository"]["author"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
|
||||
if (URL == urlOrName || NAME == urlOrName) {
|
||||
|
||||
if (identifier.matches(URL, NAME, AUTHOR)) {
|
||||
// unload the plugins!!
|
||||
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
|
||||
if (!file.path().string().ends_with(".so"))
|
||||
|
|
@ -183,7 +178,7 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
|||
// clang-format off
|
||||
auto DATA = toml::table{
|
||||
{"state", toml::table{
|
||||
{"hash", state.headersAbiCompiled},
|
||||
{"hash", state.headersHashCompiled},
|
||||
{"dont_warn_install", state.dontWarnInstall}
|
||||
}}
|
||||
};
|
||||
|
|
@ -208,8 +203,8 @@ SGlobalState DataState::getGlobalState() {
|
|||
auto DATA = toml::parse_file(stateFile.c_str());
|
||||
|
||||
SGlobalState state;
|
||||
state.headersAbiCompiled = DATA["state"]["hash"].value_or("");
|
||||
state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
|
||||
state.headersHashCompiled = DATA["state"]["hash"].value_or("");
|
||||
state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
@ -221,18 +216,16 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
|||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto AUTHOR = STATE["repository"]["author"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
const auto REV = STATE["repository"]["rev"].value_or("");
|
||||
const auto HASH = STATE["repository"]["hash"].value_or("");
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
const auto REV = STATE["repository"]["rev"].value_or("");
|
||||
const auto HASH = STATE["repository"]["hash"].value_or("");
|
||||
|
||||
SPluginRepository repo;
|
||||
repo.hash = HASH;
|
||||
repo.name = NAME;
|
||||
repo.author = AUTHOR;
|
||||
repo.url = URL;
|
||||
repo.rev = REV;
|
||||
repo.hash = HASH;
|
||||
repo.name = NAME;
|
||||
repo.url = URL;
|
||||
repo.rev = REV;
|
||||
|
||||
for (const auto& [key, val] : STATE) {
|
||||
if (key == "repository")
|
||||
|
|
@ -251,7 +244,7 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
|||
return repos;
|
||||
}
|
||||
|
||||
bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled) {
|
||||
bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
|
|
@ -260,17 +253,8 @@ bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool en
|
|||
if (key == "repository")
|
||||
continue;
|
||||
|
||||
switch (identifier.type) {
|
||||
case IDENTIFIER_NAME:
|
||||
if (key.str() != identifier.name)
|
||||
continue;
|
||||
break;
|
||||
case IDENTIFIER_AUTHOR_NAME:
|
||||
if (STATE["repository"]["author"] != identifier.author || key.str() != identifier.name)
|
||||
continue;
|
||||
break;
|
||||
default: return false;
|
||||
}
|
||||
if (key.str() != name)
|
||||
continue;
|
||||
|
||||
const auto FAILED = STATE[key]["failed"].value_or(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
#include "Plugin.hpp"
|
||||
|
||||
struct SGlobalState {
|
||||
std::string headersAbiCompiled = "";
|
||||
bool dontWarnInstall = false;
|
||||
std::string headersHashCompiled = "";
|
||||
bool dontWarnInstall = false;
|
||||
};
|
||||
|
||||
namespace DataState {
|
||||
|
|
@ -15,11 +15,11 @@ namespace DataState {
|
|||
std::vector<std::filesystem::path> getPluginStates();
|
||||
void ensureStateStoreExists();
|
||||
void addNewPluginRepo(const SPluginRepository& repo);
|
||||
void removePluginRepo(const SPluginRepoIdentifier identifier);
|
||||
bool pluginRepoExists(const SPluginRepoIdentifier identifier);
|
||||
void removePluginRepo(const std::string& urlOrName);
|
||||
bool pluginRepoExists(const std::string& urlOrName);
|
||||
void updateGlobalState(const SGlobalState& state);
|
||||
void purgeAllCache();
|
||||
SGlobalState getGlobalState();
|
||||
bool setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled);
|
||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
||||
std::vector<SPluginRepository> getAllRepositories();
|
||||
};
|
||||
};
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
#include "Plugin.hpp"
|
||||
|
||||
SPluginRepoIdentifier SPluginRepoIdentifier::fromUrl(const std::string& url) {
|
||||
return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = url};
|
||||
}
|
||||
|
||||
SPluginRepoIdentifier SPluginRepoIdentifier::fromName(const std::string& name) {
|
||||
return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = name};
|
||||
}
|
||||
|
||||
SPluginRepoIdentifier SPluginRepoIdentifier::fromAuthorName(const std::string& author, const std::string& name) {
|
||||
return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
|
||||
}
|
||||
|
||||
SPluginRepoIdentifier SPluginRepoIdentifier::fromString(const std::string& string) {
|
||||
if (string.find(':') != std::string::npos) {
|
||||
return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = string};
|
||||
} else {
|
||||
auto slashPos = string.find('/');
|
||||
if (slashPos != std::string::npos) {
|
||||
std::string author = string.substr(0, slashPos);
|
||||
std::string name = string.substr(slashPos + 1, string.size() - slashPos - 1);
|
||||
return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
|
||||
} else {
|
||||
return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = string};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string SPluginRepoIdentifier::toString() const {
|
||||
switch (type) {
|
||||
case IDENTIFIER_NAME: return name;
|
||||
case IDENTIFIER_AUTHOR_NAME: return author + '/' + name;
|
||||
case IDENTIFIER_URL: return url;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool SPluginRepoIdentifier::matches(const std::string& url, const std::string& name, const std::string& author) const {
|
||||
switch (type) {
|
||||
case IDENTIFIER_URL: return this->url == url;
|
||||
case IDENTIFIER_NAME: return this->name == name;
|
||||
case IDENTIFIER_AUTHOR_NAME: return this->author == author && this->name == name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -14,27 +14,6 @@ struct SPluginRepository {
|
|||
std::string url;
|
||||
std::string rev;
|
||||
std::string name;
|
||||
std::string author;
|
||||
std::vector<SPlugin> plugins;
|
||||
std::string hash;
|
||||
};
|
||||
|
||||
enum ePluginRepoIdentifierType {
|
||||
IDENTIFIER_URL,
|
||||
IDENTIFIER_NAME,
|
||||
IDENTIFIER_AUTHOR_NAME
|
||||
};
|
||||
|
||||
struct SPluginRepoIdentifier {
|
||||
ePluginRepoIdentifierType type;
|
||||
std::string url = "";
|
||||
std::string name = "";
|
||||
std::string author = "";
|
||||
|
||||
static SPluginRepoIdentifier fromString(const std::string& string);
|
||||
static SPluginRepoIdentifier fromUrl(const std::string& Url);
|
||||
static SPluginRepoIdentifier fromName(const std::string& name);
|
||||
static SPluginRepoIdentifier fromAuthorName(const std::string& author, const std::string& name);
|
||||
std::string toString() const;
|
||||
bool matches(const std::string& url, const std::string& name, const std::string& author) const;
|
||||
};
|
||||
};
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <print>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
|
@ -79,30 +78,40 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
|
|||
else
|
||||
onceInstalled = true;
|
||||
|
||||
const auto HLVERCALL = running ? NHyprlandSocket::send("j/version") : execAndGet("Hyprland --version-json");
|
||||
const auto HLVERCALL = running ? NHyprlandSocket::send("/version") : execAndGet("Hyprland --version");
|
||||
if (m_bVerbose)
|
||||
std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
|
||||
|
||||
auto jsonQuery = glz::read_json<glz::generic>(HLVERCALL);
|
||||
|
||||
if (!jsonQuery) {
|
||||
std::println("{}", failureString("failed to get the current hyprland version. Are you running hyprland?"));
|
||||
if (!HLVERCALL.contains("Tag:")) {
|
||||
std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
|
||||
return SHyprlandVersion{};
|
||||
}
|
||||
|
||||
auto hlbranch = (*jsonQuery)["branch"].get_string();
|
||||
auto hlcommit = (*jsonQuery)["commit"].get_string();
|
||||
auto abiHash = (*jsonQuery)["abiHash"].get_string();
|
||||
auto hldate = (*jsonQuery)["commit_date"].get_string();
|
||||
auto hlcommits = (*jsonQuery)["commits"].get_string();
|
||||
std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10);
|
||||
hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' '));
|
||||
|
||||
size_t commits = 0;
|
||||
std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12);
|
||||
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
|
||||
|
||||
std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
|
||||
hldate = hldate.substr(0, hldate.find('\n'));
|
||||
|
||||
std::string hlcommits;
|
||||
|
||||
if (HLVERCALL.contains("commits:")) {
|
||||
hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9);
|
||||
hlcommits = hlcommits.substr(0, hlcommits.find(' '));
|
||||
}
|
||||
|
||||
int commits = 0;
|
||||
try {
|
||||
commits = std::stoull(hlcommits);
|
||||
commits = std::stoi(hlcommits);
|
||||
} catch (...) { ; }
|
||||
|
||||
if (m_bVerbose)
|
||||
std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
|
||||
|
||||
auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, abiHash, commits};
|
||||
auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
|
||||
|
||||
if (running)
|
||||
verRunning = ver;
|
||||
|
|
@ -137,7 +146,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
return false;
|
||||
}
|
||||
|
||||
if (DataState::pluginRepoExists(SPluginRepoIdentifier::fromUrl(url))) {
|
||||
if (DataState::pluginRepoExists(url)) {
|
||||
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -152,7 +161,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
DataState::updateGlobalState(GLOBALSTATE);
|
||||
}
|
||||
|
||||
if (GLOBALSTATE.headersAbiCompiled.empty()) {
|
||||
if (GLOBALSTATE.headersHashCompiled.empty()) {
|
||||
std::println("\n{}", failureString("Cannot find headers in the global state. Try running hyprpm update first."));
|
||||
return false;
|
||||
}
|
||||
|
|
@ -334,13 +343,10 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
||||
if (repohash.length() > 0)
|
||||
repohash.pop_back();
|
||||
auto lastSlash = url.find_last_of('/');
|
||||
auto secondLastSlash = url.find_last_of('/', lastSlash - 1);
|
||||
repo.name = pManifest->m_repository.name.empty() ? url.substr(lastSlash + 1) : pManifest->m_repository.name;
|
||||
repo.author = url.substr(secondLastSlash + 1, lastSlash - secondLastSlash - 1);
|
||||
repo.url = url;
|
||||
repo.rev = rev;
|
||||
repo.hash = repohash;
|
||||
repo.name = pManifest->m_repository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_repository.name;
|
||||
repo.url = url;
|
||||
repo.rev = rev;
|
||||
repo.hash = repohash;
|
||||
for (auto const& p : pManifest->m_plugins) {
|
||||
repo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, false, p.failed});
|
||||
}
|
||||
|
|
@ -360,13 +366,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) {
|
||||
if (!DataState::pluginRepoExists(identifier)) {
|
||||
bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
|
||||
if (!DataState::pluginRepoExists(urlOrName)) {
|
||||
std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << identifier.toString() << "\n "
|
||||
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n "
|
||||
<< "Are you sure? [Y/n] ";
|
||||
std::fflush(stdout);
|
||||
std::string input;
|
||||
|
|
@ -377,7 +383,7 @@ bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) {
|
|||
return false;
|
||||
}
|
||||
|
||||
DataState::removePluginRepo(identifier);
|
||||
DataState::removePluginRepo(urlOrName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -438,16 +444,11 @@ eHeadersErrors CPluginManager::headersValid() {
|
|||
if (hash != HLVER.hash)
|
||||
return HEADERS_MISMATCHED;
|
||||
|
||||
// check ABI hash too
|
||||
const auto GLOBALSTATE = DataState::getGlobalState();
|
||||
|
||||
if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash)
|
||||
return HEADERS_ABI_MISMATCH;
|
||||
|
||||
return HEADERS_OK;
|
||||
}
|
||||
|
||||
bool CPluginManager::updateHeaders(bool force) {
|
||||
|
||||
DataState::ensureStateStoreExists();
|
||||
|
||||
const auto HLVER = getHyprlandVersion(false);
|
||||
|
|
@ -588,15 +589,14 @@ bool CPluginManager::updateHeaders(bool force) {
|
|||
std::filesystem::remove_all(WORKINGDIR);
|
||||
|
||||
auto HEADERSVALID = headersValid();
|
||||
|
||||
if (HEADERSVALID == HEADERS_OK || HEADERSVALID == HEADERS_MISMATCHED || HEADERSVALID == HEADERS_ABI_MISMATCH) {
|
||||
if (HEADERSVALID == HEADERS_OK) {
|
||||
progress.printMessageAbove(successString("installed headers"));
|
||||
progress.m_iSteps = 5;
|
||||
progress.m_szCurrentMessage = "Done!";
|
||||
progress.print();
|
||||
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
||||
DataState::updateGlobalState(GLOBALSTATE);
|
||||
|
||||
std::print("\n");
|
||||
|
|
@ -775,7 +775,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
|||
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
|
||||
newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
|
||||
}
|
||||
DataState::removePluginRepo(SPluginRepoIdentifier::fromName(newrepo.name));
|
||||
DataState::removePluginRepo(newrepo.name);
|
||||
DataState::addNewPluginRepo(newrepo);
|
||||
|
||||
std::filesystem::remove_all(m_szWorkingPluginDirectory);
|
||||
|
|
@ -787,8 +787,8 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
|||
progress.m_szCurrentMessage = "Updating global state...";
|
||||
progress.print();
|
||||
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
||||
DataState::updateGlobalState(GLOBALSTATE);
|
||||
|
||||
progress.m_iSteps++;
|
||||
|
|
@ -800,23 +800,17 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CPluginManager::enablePlugin(const SPluginRepoIdentifier identifier) {
|
||||
bool ret = false;
|
||||
|
||||
switch (identifier.type) {
|
||||
case IDENTIFIER_NAME:
|
||||
case IDENTIFIER_AUTHOR_NAME: ret = DataState::setPluginEnabled(identifier, true); break;
|
||||
default: return false;
|
||||
}
|
||||
bool CPluginManager::enablePlugin(const std::string& name) {
|
||||
bool ret = DataState::setPluginEnabled(name, true);
|
||||
if (ret)
|
||||
std::println("{}", successString("Enabled {}", identifier.name));
|
||||
std::println("{}", successString("Enabled {}", name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CPluginManager::disablePlugin(const SPluginRepoIdentifier identifier) {
|
||||
bool ret = DataState::setPluginEnabled(identifier, false);
|
||||
bool CPluginManager::disablePlugin(const std::string& name) {
|
||||
bool ret = DataState::setPluginEnabled(name, false);
|
||||
if (ret)
|
||||
std::println("{}", successString("Disabled {}", identifier.name));
|
||||
std::println("{}", successString("Disabled {}", name));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -834,7 +828,7 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload)
|
|||
}
|
||||
const auto HYPRPMPATH = DataState::getDataStatePath();
|
||||
|
||||
const auto json = glz::read_json<glz::generic::array_t>(NHyprlandSocket::send("j/plugins list"));
|
||||
const auto json = glz::read_json<glz::json_t::array_t>(NHyprlandSocket::send("j/plugins list"));
|
||||
if (!json) {
|
||||
std::println(stderr, "PluginManager: couldn't parse plugin list output");
|
||||
return LOADSTATE_FAIL;
|
||||
|
|
@ -919,9 +913,9 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
|
|||
auto state = DataState::getGlobalState();
|
||||
auto HLVER = getHyprlandVersion(true);
|
||||
|
||||
if (state.headersAbiCompiled != HLVER.abiHash) {
|
||||
if (state.headersHashCompiled != HLVER.hash) {
|
||||
if (load)
|
||||
std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersAbiCompiled));
|
||||
std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersHashCompiled));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -937,7 +931,7 @@ void CPluginManager::listAllPlugins() {
|
|||
const auto REPOS = DataState::getAllRepositories();
|
||||
|
||||
for (auto const& r : REPOS) {
|
||||
std::println("{}", infoString("Repository {} (by {}):", r.name, r.author));
|
||||
std::println("{}", infoString("Repository {}:", r.name));
|
||||
|
||||
for (auto const& p : r.plugins) {
|
||||
std::println(" │ Plugin {}", p.name);
|
||||
|
|
@ -962,7 +956,6 @@ std::string CPluginManager::headerError(const eHeadersErrors err) {
|
|||
case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n");
|
||||
case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n");
|
||||
case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
|
||||
case HEADERS_ABI_MISMATCH: return failureString("ABI is mismatched. Please run hyprpm update to fix that.\n");
|
||||
case HEADERS_DUPLICATED: {
|
||||
return failureString("Headers duplicated!!! This is a very bad sign.\n"
|
||||
"This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n"
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "Plugin.hpp"
|
||||
|
||||
enum eHeadersErrors {
|
||||
HEADERS_OK = 0,
|
||||
|
|
@ -13,7 +11,6 @@ enum eHeadersErrors {
|
|||
HEADERS_MISSING,
|
||||
HEADERS_CORRUPTED,
|
||||
HEADERS_MISMATCHED,
|
||||
HEADERS_ABI_MISMATCH,
|
||||
HEADERS_DUPLICATED
|
||||
};
|
||||
|
||||
|
|
@ -39,7 +36,6 @@ struct SHyprlandVersion {
|
|||
std::string branch;
|
||||
std::string hash;
|
||||
std::string date;
|
||||
std::string abiHash;
|
||||
int commits = 0;
|
||||
};
|
||||
|
||||
|
|
@ -48,7 +44,7 @@ class CPluginManager {
|
|||
CPluginManager();
|
||||
|
||||
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
||||
bool removePluginRepo(const SPluginRepoIdentifier identifier);
|
||||
bool removePluginRepo(const std::string& urlOrName);
|
||||
|
||||
eHeadersErrors headersValid();
|
||||
bool updateHeaders(bool force = false);
|
||||
|
|
@ -56,8 +52,8 @@ class CPluginManager {
|
|||
|
||||
void listAllPlugins();
|
||||
|
||||
bool enablePlugin(const SPluginRepoIdentifier identifier);
|
||||
bool disablePlugin(const SPluginRepoIdentifier identifier);
|
||||
bool enablePlugin(const std::string& name);
|
||||
bool disablePlugin(const std::string& name);
|
||||
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
|
||||
|
||||
bool loadUnloadPlugin(const std::string& path, bool load);
|
||||
|
|
|
|||
|
|
@ -13,25 +13,25 @@ using namespace Hyprutils::Utils;
|
|||
|
||||
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
||||
┃
|
||||
┣ add <url> [git rev] → Install a new plugin repository from git. Git revision
|
||||
┃ is optional, when set, commit locks are ignored.
|
||||
┣ remove <url|name|author/name> → Remove an installed plugin repository.
|
||||
┣ enable <name|author/name> → Enable a plugin.
|
||||
┣ disable <name|author/name> → Disable a plugin.
|
||||
┣ update → Check and update all plugins if needed.
|
||||
┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
|
||||
┣ list → List all installed plugins.
|
||||
┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers.
|
||||
┣ add [url] [git rev] → Install a new plugin repository from git. Git revision
|
||||
┃ is optional, when set, commit locks are ignored.
|
||||
┣ remove [url/name] → Remove an installed plugin repository.
|
||||
┣ enable [name] → Enable a plugin.
|
||||
┣ disable [name] → Disable a plugin.
|
||||
┣ update → Check and update all plugins if needed.
|
||||
┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
|
||||
┣ list → List all installed plugins.
|
||||
┣ purge-cache → Remove the entire hyprpm cache, built plugins, hyprpm settings and headers.
|
||||
┃
|
||||
┣ Flags:
|
||||
┃
|
||||
┣ --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).
|
||||
┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources.
|
||||
┣ --hl-url | → Pass a custom hyprland source url.
|
||||
┣ --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.
|
||||
┣ --help | -h → Show this menu.
|
||||
┣ --verbose | -v → Enable too much logging.
|
||||
┣ --force | -f → Force an operation ignoring checks (e.g. update -f).
|
||||
┣ --no-shallow | -s → Disable shallow cloning of Hyprland sources.
|
||||
┣ --hl-url | → Pass a custom hyprland source url.
|
||||
┗
|
||||
)#";
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
}
|
||||
|
||||
std::vector<std::string> command;
|
||||
bool notify = false, verbose = false, force = false, noShallow = false;
|
||||
bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
|
||||
std::string customHlUrl;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
|
|
@ -58,9 +58,7 @@ 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") {
|
||||
// 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.");
|
||||
notifyFail = notify = true;
|
||||
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
|
||||
verbose = true;
|
||||
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
|
||||
|
|
@ -106,7 +104,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
const auto HLVER = g_pPluginManager->getHyprlandVersion();
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
|
||||
if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash) {
|
||||
if (GLOBALSTATE.headersHashCompiled != HLVER.hash) {
|
||||
std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update."));
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -126,7 +124,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
return g_pPluginManager->removePluginRepo(SPluginRepoIdentifier::fromString(command[1])) ? 0 : 1;
|
||||
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
|
||||
} else if (command[0] == "update") {
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
|
@ -137,7 +135,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
if (headers) {
|
||||
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
const auto COMPILEDOUTDATED = HLVER.abiHash != GLOBALSTATE.headersAbiCompiled;
|
||||
const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled;
|
||||
|
||||
bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED);
|
||||
|
||||
|
|
@ -151,16 +149,15 @@ int main(int argc, char** argv, char** envp) {
|
|||
|
||||
if (ret2 != LOADSTATE_OK)
|
||||
return 1;
|
||||
} else {
|
||||
} else if (notify)
|
||||
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."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!g_pPluginManager->enablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
|
||||
if (!g_pPluginManager->enablePlugin(command[1])) {
|
||||
std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -181,7 +178,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!g_pPluginManager->disablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
|
||||
if (!g_pPluginManager->disablePlugin(command[1])) {
|
||||
std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -197,17 +194,19 @@ int main(int argc, char** argv, char** envp) {
|
|||
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
|
||||
|
||||
if (ret != LOADSTATE_OK) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else if (notify) {
|
||||
} else if (notify && !notifyFail) {
|
||||
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
|
||||
}
|
||||
} else if (command[0] == "purge-cache") {
|
||||
|
|
|
|||
32
hyprpm/src/meson.build
Normal file
32
hyprpm/src/meson.build
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
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',
|
||||
)
|
||||
|
|
@ -99,4 +99,3 @@ protocolnew("stable/xdg-shell" "xdg-shell" false)
|
|||
|
||||
clientNew("pointer-warp" PROTOS "pointer-warp-v1" "xdg-shell")
|
||||
clientNew("pointer-scroll" PROTOS "xdg-shell")
|
||||
clientNew("child-window" PROTOS "xdg-shell")
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
#include <print>
|
||||
#include <poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland.hpp>
|
||||
#include <xdg-shell.hpp>
|
||||
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
|
||||
using Hyprutils::Math::Vector2D;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
struct SWlState {
|
||||
wl_display* display;
|
||||
CSharedPointer<CCWlRegistry> registry;
|
||||
|
||||
// protocols
|
||||
CSharedPointer<CCWlCompositor> wlCompositor;
|
||||
CSharedPointer<CCWlSeat> wlSeat;
|
||||
CSharedPointer<CCWlShm> wlShm;
|
||||
CSharedPointer<CCXdgWmBase> xdgShell;
|
||||
|
||||
// shm/buffer stuff
|
||||
CSharedPointer<CCWlShmPool> shmPool;
|
||||
CSharedPointer<CCWlBuffer> shmBuf;
|
||||
CSharedPointer<CCWlBuffer> shmBuf2;
|
||||
int shmFd = 0;
|
||||
size_t shmBufSize = 0;
|
||||
bool xrgb8888_support = false;
|
||||
|
||||
// surface/toplevel stuff
|
||||
CSharedPointer<CCWlSurface> surf;
|
||||
CSharedPointer<CCXdgSurface> xdgSurf;
|
||||
CSharedPointer<CCXdgToplevel> xdgToplevel;
|
||||
Vector2D geom;
|
||||
|
||||
// pointer
|
||||
CSharedPointer<CCWlPointer> pointer;
|
||||
uint32_t enterSerial = 0;
|
||||
};
|
||||
|
||||
bool debug, shouldExit, started;
|
||||
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
static void clientLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
std::println("{}", text);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
static void debugLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
if (!debug)
|
||||
return;
|
||||
std::println("{}", text);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
static bool bindRegistry(SWlState& state) {
|
||||
state.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(state.display));
|
||||
|
||||
state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) {
|
||||
const std::string NAME = name;
|
||||
if (NAME == "wl_compositor") {
|
||||
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||
state.wlCompositor = makeShared<CCWlCompositor>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6));
|
||||
} else if (NAME == "wl_shm") {
|
||||
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||
state.wlShm = makeShared<CCWlShm>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1));
|
||||
} else if (NAME == "wl_seat") {
|
||||
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||
state.wlSeat = makeShared<CCWlSeat>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9));
|
||||
} else if (NAME == "xdg_wm_base") {
|
||||
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||
state.xdgShell = makeShared<CCXdgWmBase>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1));
|
||||
}
|
||||
});
|
||||
state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); });
|
||||
|
||||
wl_display_roundtrip(state.display);
|
||||
|
||||
if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell) {
|
||||
clientLog("Failed to get protocols from Hyprland");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool createShm(SWlState& state, Vector2D geom) {
|
||||
if (!state.xrgb8888_support)
|
||||
return false;
|
||||
|
||||
size_t stride = geom.x * 4;
|
||||
size_t size = geom.y * stride;
|
||||
if (!state.shmPool) {
|
||||
const char* name = "/wl-shm-pointer-warp";
|
||||
state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
if (state.shmFd < 0)
|
||||
return false;
|
||||
|
||||
if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size * 2) < 0) {
|
||||
close(state.shmFd);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.shmPool = makeShared<CCWlShmPool>(state.wlShm->sendCreatePool(state.shmFd, size * 2));
|
||||
if (!state.shmPool->resource()) {
|
||||
close(state.shmFd);
|
||||
state.shmFd = -1;
|
||||
state.shmPool.reset();
|
||||
return false;
|
||||
}
|
||||
state.shmBufSize = size;
|
||||
} else if (size > state.shmBufSize) {
|
||||
if (ftruncate(state.shmFd, size) < 0) {
|
||||
close(state.shmFd);
|
||||
state.shmFd = -1;
|
||||
state.shmPool.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
state.shmPool->sendResize(size * 2);
|
||||
state.shmBufSize = size;
|
||||
}
|
||||
|
||||
auto buf = makeShared<CCWlBuffer>(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888));
|
||||
if (!buf->resource())
|
||||
return false;
|
||||
|
||||
if (state.shmBuf) {
|
||||
state.shmBuf->sendDestroy();
|
||||
state.shmBuf.reset();
|
||||
}
|
||||
state.shmBuf = buf;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setupToplevel(SWlState& state) {
|
||||
state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) {
|
||||
if (format == WL_SHM_FORMAT_XRGB8888)
|
||||
state.xrgb8888_support = true;
|
||||
});
|
||||
|
||||
state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); });
|
||||
|
||||
state.surf = makeShared<CCWlSurface>(state.wlCompositor->sendCreateSurface());
|
||||
if (!state.surf->resource())
|
||||
return false;
|
||||
|
||||
state.xdgSurf = makeShared<CCXdgSurface>(state.xdgShell->sendGetXdgSurface(state.surf->resource()));
|
||||
if (!state.xdgSurf->resource())
|
||||
return false;
|
||||
|
||||
state.xdgToplevel = makeShared<CCXdgToplevel>(state.xdgSurf->sendGetToplevel());
|
||||
if (!state.xdgToplevel->resource())
|
||||
return false;
|
||||
|
||||
state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); });
|
||||
|
||||
state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) {
|
||||
state.geom = {1280, 720};
|
||||
|
||||
if (!createShm(state, state.geom))
|
||||
exit(-1);
|
||||
});
|
||||
|
||||
state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) {
|
||||
if (!state.shmBuf)
|
||||
debugLog("xdgSurf configure but no buf made yet?");
|
||||
|
||||
state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y);
|
||||
state.surf->sendAttach(state.shmBuf.get(), 0, 0);
|
||||
state.surf->sendCommit();
|
||||
|
||||
state.xdgSurf->sendAckConfigure(serial);
|
||||
|
||||
if (!started) {
|
||||
started = true;
|
||||
clientLog("started");
|
||||
}
|
||||
});
|
||||
|
||||
state.xdgToplevel->sendSetTitle("child-test parent");
|
||||
state.xdgToplevel->sendSetAppId("child-test-parent");
|
||||
|
||||
state.surf->sendAttach(nullptr, 0, 0);
|
||||
state.surf->sendCommit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setupSeat(SWlState& state) {
|
||||
state.pointer = makeShared<CCWlPointer>(state.wlSeat->sendGetPointer());
|
||||
if (!state.pointer->resource())
|
||||
return false;
|
||||
|
||||
state.pointer->setEnter([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf, wl_fixed_t x, wl_fixed_t y) {
|
||||
debugLog("Got pointer enter event, serial {}, x {}, y {}", serial, x, y);
|
||||
state.enterSerial = serial;
|
||||
});
|
||||
|
||||
state.pointer->setLeave([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf) { debugLog("Got pointer leave event, serial {}", serial); });
|
||||
|
||||
state.pointer->setMotion([&](CCWlPointer* p, uint32_t serial, wl_fixed_t x, wl_fixed_t y) { debugLog("Got pointer motion event, serial {}, x {}, y {}", serial, x, y); });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct SChildWindow {
|
||||
CSharedPointer<CCWlSurface> surface;
|
||||
CSharedPointer<CCXdgSurface> xSurface;
|
||||
CSharedPointer<CCXdgToplevel> toplevel;
|
||||
};
|
||||
|
||||
static void parseRequest(SWlState& state, std::string str, SChildWindow& window) {
|
||||
if (str.starts_with("exit")) {
|
||||
shouldExit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t index = str.find_first_of('\n');
|
||||
str = str.substr(0, index);
|
||||
|
||||
if (str == "toplevel") {
|
||||
window.surface = makeShared<CCWlSurface>(state.wlCompositor->sendCreateSurface());
|
||||
window.xSurface = makeShared<CCXdgSurface>(state.xdgShell->sendGetXdgSurface(window.surface->resource()));
|
||||
|
||||
window.xSurface->setConfigure([&](CCXdgSurface* p, uint32_t serial) {
|
||||
if (!state.shmBuf)
|
||||
debugLog("xdgSurf configure but no buf made yet?");
|
||||
|
||||
window.xSurface->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y);
|
||||
window.surface->sendAttach(state.shmBuf2.get(), 0, 0);
|
||||
window.surface->sendCommit();
|
||||
|
||||
window.xSurface->sendAckConfigure(serial);
|
||||
});
|
||||
|
||||
window.toplevel = makeShared<CCXdgToplevel>(window.xSurface->sendGetToplevel());
|
||||
|
||||
window.toplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) {
|
||||
size_t stride = 1280 * 4;
|
||||
size_t size = 720 * stride;
|
||||
|
||||
auto buf = makeShared<CCWlBuffer>(state.shmPool->sendCreateBuffer(size, state.geom.x, state.geom.y, stride, WL_SHM_FORMAT_XRGB8888));
|
||||
if (!buf->resource())
|
||||
clientLog("Failed to create child buffer");
|
||||
|
||||
if (state.shmBuf2) {
|
||||
state.shmBuf2->sendDestroy();
|
||||
state.shmBuf2.reset();
|
||||
}
|
||||
state.shmBuf2 = buf;
|
||||
});
|
||||
|
||||
window.toplevel->sendSetTitle("child-test child");
|
||||
window.toplevel->sendSetAppId("child-test-child");
|
||||
window.toplevel->sendSetParent(state.xdgToplevel.get());
|
||||
|
||||
window.surface->sendAttach(nullptr, 0, 0);
|
||||
window.surface->sendCommit();
|
||||
clientLog("child started");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 1 && argc != 2)
|
||||
clientLog("Only the \"--debug\" switch is allowed, it turns on debug logs.");
|
||||
|
||||
if (argc == 2 && std::string{argv[1]} == "--debug")
|
||||
debug = true;
|
||||
|
||||
SWlState state;
|
||||
SChildWindow window;
|
||||
|
||||
// WAYLAND_DISPLAY env should be set to the correct one
|
||||
state.display = wl_display_connect(nullptr);
|
||||
if (!state.display) {
|
||||
clientLog("Failed to connect to wayland display");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!bindRegistry(state) || !setupSeat(state) || !setupToplevel(state))
|
||||
return -1;
|
||||
|
||||
std::array<char, 1024> readBuf;
|
||||
readBuf.fill(0);
|
||||
|
||||
wl_display_flush(state.display);
|
||||
|
||||
struct pollfd fds[2] = {{.fd = wl_display_get_fd(state.display), .events = POLLIN | POLLOUT}, {.fd = STDIN_FILENO, .events = POLLIN}};
|
||||
while (!shouldExit && poll(fds, 2, 0) != -1) {
|
||||
if (fds[0].revents & POLLIN) {
|
||||
wl_display_flush(state.display);
|
||||
|
||||
if (wl_display_prepare_read(state.display) == 0) {
|
||||
wl_display_read_events(state.display);
|
||||
wl_display_dispatch_pending(state.display);
|
||||
} else
|
||||
wl_display_dispatch(state.display);
|
||||
|
||||
int ret = 0;
|
||||
do {
|
||||
ret = wl_display_dispatch_pending(state.display);
|
||||
wl_display_flush(state.display);
|
||||
} while (ret > 0);
|
||||
}
|
||||
|
||||
if (fds[1].revents & POLLIN) {
|
||||
ssize_t bytesRead = read(fds[1].fd, readBuf.data(), 1023);
|
||||
if (bytesRead == -1)
|
||||
continue;
|
||||
readBuf[bytesRead] = 0;
|
||||
|
||||
parseRequest(state, std::string{readBuf.data()}, window);
|
||||
}
|
||||
}
|
||||
|
||||
wl_display* display = state.display;
|
||||
state = {};
|
||||
window = {};
|
||||
|
||||
wl_display_disconnect(display);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -11,10 +11,7 @@
|
|||
#include <src/managers/input/InputManager.hpp>
|
||||
#include <src/managers/PointerManager.hpp>
|
||||
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
||||
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
|
||||
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
||||
#include <src/Compositor.hpp>
|
||||
#include <src/desktop/state/FocusState.hpp>
|
||||
#undef private
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
|
@ -46,7 +43,7 @@ static SDispatchResult test(std::string in) {
|
|||
|
||||
// Trigger a snap move event for the active window
|
||||
static SDispatchResult snapMove(std::string in) {
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
const auto PLASTWINDOW = g_pCompositor->m_lastWindow.lock();
|
||||
if (!PLASTWINDOW->m_isFloating)
|
||||
return {.success = false, .error = "Window must be floating"};
|
||||
|
||||
|
|
@ -123,7 +120,6 @@ class CTestMouse : public IPointer {
|
|||
};
|
||||
|
||||
SP<CTestMouse> g_mouse;
|
||||
SP<CTestKeyboard> g_keyboard;
|
||||
|
||||
static SDispatchResult pressAlt(std::string in) {
|
||||
g_pInputManager->m_lastMods = in == "1" ? HL_MODIFIER_ALT : 0;
|
||||
|
|
@ -208,12 +204,12 @@ static SDispatchResult vkb(std::string in) {
|
|||
}
|
||||
|
||||
static SDispatchResult scroll(std::string in) {
|
||||
double by;
|
||||
int by;
|
||||
try {
|
||||
by = std::stod(in);
|
||||
by = std::stoi(in);
|
||||
} catch (...) { return SDispatchResult{.success = false, .error = "invalid input"}; }
|
||||
|
||||
Log::logger->log(Log::DEBUG, "tester: scrolling by {}", by);
|
||||
Debug::log(LOG, "tester: scrolling by {}", by);
|
||||
|
||||
g_mouse->m_pointerEvents.axis.emit(IPointer::SAxisEvent{
|
||||
.delta = by,
|
||||
|
|
@ -224,56 +220,6 @@ static SDispatchResult scroll(std::string in) {
|
|||
return {};
|
||||
}
|
||||
|
||||
static SDispatchResult keybind(std::string in) {
|
||||
CVarList2 data(std::move(in));
|
||||
// 0 = release, 1 = press
|
||||
bool press;
|
||||
// See src/devices/IKeyboard.hpp : eKeyboardModifiers for modifier bitmasks
|
||||
// 0 = none, eKeyboardModifiers is shifted to start at 1
|
||||
uint32_t modifier;
|
||||
// keycode
|
||||
uint32_t key;
|
||||
try {
|
||||
press = std::stoul(std::string{data[0]}) == 1;
|
||||
modifier = std::stoul(std::string{data[1]});
|
||||
key = std::stoul(std::string{data[2]}) - 8; // xkb offset
|
||||
} catch (...) { return {.success = false, .error = "invalid input"}; }
|
||||
|
||||
uint32_t modifierMask = 0;
|
||||
if (modifier > 0)
|
||||
modifierMask = 1 << (modifier - 1);
|
||||
g_pInputManager->m_lastMods = modifierMask;
|
||||
g_keyboard->sendKey(key, press);
|
||||
|
||||
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) {
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
|
||||
if (!PLASTWINDOW)
|
||||
return {.success = false, .error = "No window"};
|
||||
|
||||
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
|
||||
return {.success = false, .error = "No rule"};
|
||||
|
||||
if (PLASTWINDOW->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;
|
||||
|
||||
|
|
@ -283,24 +229,15 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt);
|
||||
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);
|
||||
g_pInputManager->newMouse(g_mouse);
|
||||
|
||||
// init keyboard
|
||||
g_keyboard = CTestKeyboard::create(false);
|
||||
g_pInputManager->newKeyboard(g_keyboard);
|
||||
|
||||
return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"};
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT() {
|
||||
g_mouse->destroy();
|
||||
g_mouse.reset();
|
||||
g_keyboard->destroy();
|
||||
g_keyboard.reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,17 +18,6 @@ namespace Colors {
|
|||
constexpr const char* RESET = "\x1b[0m";
|
||||
};
|
||||
|
||||
#define EXPECT_MAX_DELTA(expr, desired, delta) \
|
||||
if (const auto RESULT = expr; std::abs(RESULT - (desired)) > delta) { \
|
||||
NLog::log("{}Failed: {}{}, expected max delta of {}, got delta {} ({} - {}). Source: {}@{}.", Colors::RED, Colors::RESET, #expr, delta, (RESULT - (desired)), RESULT, \
|
||||
desired, __FILE__, __LINE__); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, (RESULT - (desired))); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT(expr, val) \
|
||||
if (const auto RESULT = expr; RESULT != (val)) { \
|
||||
NLog::log("{}Failed: {}{}, expected {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "../shared.hpp"
|
||||
#include "tests.hpp"
|
||||
#include "build.hpp"
|
||||
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
|
||||
#include <sys/poll.h>
|
||||
#include <csignal>
|
||||
#include <thread>
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define SP CSharedPointer
|
||||
|
||||
struct SClient {
|
||||
SP<CProcess> proc;
|
||||
std::array<char, 1024> readBuf;
|
||||
CFileDescriptor readFd, writeFd;
|
||||
struct pollfd fds;
|
||||
};
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static bool waitForWindow(SP<CProcess> proc, int windowsBefore) {
|
||||
int counter = 0;
|
||||
while (Tests::processAlive(proc->pid()) && Tests::windowCount() == windowsBefore) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50)
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Waited {} milliseconds for window to open", Colors::YELLOW, counter * 100);
|
||||
return Tests::processAlive(proc->pid());
|
||||
}
|
||||
|
||||
static bool startClient(SClient& client) {
|
||||
NLog::log("{}Attempting to start child-window client", Colors::YELLOW);
|
||||
|
||||
client.proc = makeShared<CProcess>(binaryDir + "/child-window", std::vector<std::string>{});
|
||||
|
||||
client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
||||
|
||||
int procInPipeFd[2], procOutPipeFd[2];
|
||||
if (pipe(procInPipeFd) != 0 || pipe(procOutPipeFd) != 0) {
|
||||
NLog::log("{}Unable to open pipe to client", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
client.writeFd = CFileDescriptor(procInPipeFd[1]);
|
||||
client.proc->setStdinFD(procInPipeFd[0]);
|
||||
|
||||
client.readFd = CFileDescriptor(procOutPipeFd[0]);
|
||||
client.proc->setStdoutFD(procOutPipeFd[1]);
|
||||
|
||||
if (!client.proc->runAsync()) {
|
||||
NLog::log("{}Failed to run client", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
close(procInPipeFd[0]);
|
||||
close(procOutPipeFd[1]);
|
||||
|
||||
if (!waitForWindow(client.proc, Tests::windowCount())) {
|
||||
NLog::log("{}Window took too long to open", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Started child-window client", Colors::YELLOW);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void stopClient(SClient& client) {
|
||||
std::string cmd = "exit\n";
|
||||
write(client.writeFd.get(), cmd.c_str(), cmd.length());
|
||||
|
||||
kill(client.proc->pid(), SIGKILL);
|
||||
client.proc.reset();
|
||||
}
|
||||
|
||||
static bool createChild(SClient& client) {
|
||||
std::string cmd = "toplevel\n";
|
||||
if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length())
|
||||
return false;
|
||||
|
||||
if (!waitForWindow(client.proc, Tests::windowCount()))
|
||||
NLog::log("{}Child window took too long to open", Colors::RED);
|
||||
|
||||
if (getFromSocket("/dispatch focuswindow class:child-test-child") != "ok") {
|
||||
NLog::log("{}Failed to focus child window", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
SClient client;
|
||||
|
||||
if (!startClient(client))
|
||||
return false;
|
||||
OK(getFromSocket("/dispatch setfloating class:child-test-parent"));
|
||||
OK(getFromSocket("/dispatch pin class:child-test-parent"));
|
||||
|
||||
createChild(client);
|
||||
EXPECT(Tests::windowCount(), 2)
|
||||
EXPECT_COUNT_STRING(getFromSocket("/clients"), "pinned: 1", 2);
|
||||
|
||||
stopClient(client);
|
||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||
OK(getFromSocket("/reload"));
|
||||
Tests::killAllWindows();
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_CLIENT_TEST_FN(test);
|
||||
|
|
@ -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:{} no_anim 1", client.proc->pid())) != "ok") {
|
||||
if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 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 scroll_mouse 4"), "ok");
|
||||
EXPECT(getFromSocket("r/dispatch setprop active scrollmouse 4"), "ok");
|
||||
EXPECT(sendScroll(10), true);
|
||||
EXPECT(getLastDelta(client), 40);
|
||||
|
||||
|
|
|
|||
|
|
@ -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:{} no_anim 1", client.proc->pid())) != "ok") {
|
||||
if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") {
|
||||
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing hyprctl monitors", Colors::GREEN);
|
||||
|
||||
std::string monitorsSpec = getFromSocket("j/monitors");
|
||||
EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "srgb")");
|
||||
|
||||
EXPECT_CONTAINS(getFromSocket("/keyword monitor HEADLESS-2,1920x1080x60.00000,0x0,1.0,bitdepth,10,cm,wide"), "ok")
|
||||
monitorsSpec = getFromSocket("j/monitors");
|
||||
EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "wide")");
|
||||
|
||||
EXPECT_CONTAINS(getFromSocket("/keyword monitor HEADLESS-2,1920x1080x60.00000,0x0,1.0,bitdepth,10,cm,srgb,sdrbrightness,1.2,sdrsaturation,0.98"), "ok")
|
||||
monitorsSpec = getFromSocket("j/monitors");
|
||||
EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "srgb")");
|
||||
EXPECT_CONTAINS(monitorsSpec, R"("sdrBrightness": 1.20)");
|
||||
EXPECT_CONTAINS(monitorsSpec, R"("sdrSaturation": 0.98)");
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#include "../shared.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "tests.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static void testFloatClamp() {
|
||||
for (auto const& win : {"a", "b", "c"}) {
|
||||
if (!Tests::spawnKitty(win)) {
|
||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||
++TESTS_FAILED;
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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"));
|
||||
OK(getFromSocket("/dispatch settiled class:c"));
|
||||
OK(getFromSocket("/dispatch setfloating class:c"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "at: 698,158");
|
||||
EXPECT_CONTAINS(str, "size: 1200,900");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword dwindle:force_split 0"));
|
||||
|
||||
// clean up
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing Dwindle layout", Colors::GREEN);
|
||||
|
||||
// test
|
||||
NLog::log("{}Testing float clamp", Colors::GREEN);
|
||||
testFloatClamp();
|
||||
|
||||
// clean up
|
||||
NLog::log("Cleaning up", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace 1");
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test);
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing process spawning", Colors::GREEN);
|
||||
|
||||
// Note: POSIX sleep does not support fractional seconds, so
|
||||
// can't sleep for less than 1 second.
|
||||
OK(getFromSocket("/dispatch exec sleep 1"));
|
||||
|
||||
// Ensure that sleep is our child
|
||||
const std::string sleepPidS = Tests::execAndGet("pgrep sleep");
|
||||
pid_t sleepPid;
|
||||
try {
|
||||
sleepPid = std::stoull(sleepPidS);
|
||||
} catch (...) {
|
||||
NLog::log("{}Sleep was not spawned or several sleeps are running: pgrep returned '{}'", Colors::RED, sleepPidS);
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string sleepParentComm = Tests::execAndGet("cat \"/proc/$(ps -o ppid:1= -p " + sleepPidS + ")/comm\"");
|
||||
NLog::log("{}Expecting that sleep's parent is Hyprland", Colors::YELLOW);
|
||||
EXPECT_CONTAINS(sleepParentComm, "Hyprland");
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
// Ensure that sleep did not become a zombie
|
||||
EXPECT(Tests::processAlive(sleepPid), false);
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
||||
|
|
@ -18,20 +18,6 @@ using namespace Hyprutils::Memory;
|
|||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool waitForWindowCount(int expectedWindowCnt, std::string_view expectation, int waitMillis = 100, int maxWaitCnt = 50) {
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != expectedWindowCnt) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(waitMillis));
|
||||
|
||||
if (counter > maxWaitCnt) {
|
||||
NLog::log("{}Unmet expectation: {}", Colors::RED, expectation);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing gestures", Colors::GREEN);
|
||||
|
||||
|
|
@ -41,25 +27,19 @@ static bool test() {
|
|||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace 1"); // no OK: we might be on 1 already
|
||||
|
||||
Tests::spawnKitty();
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
// Give the shell a moment to initialize
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture up,5"));
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture down,5"));
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture left,5"));
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture right,5"));
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture right,4"));
|
||||
|
||||
EXPECT(waitForWindowCount(0, "Gesture sent paste exit + enter to kitty"), true);
|
||||
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture left,3"));
|
||||
|
||||
EXPECT(waitForWindowCount(1, "Gesture spawned kitty"), true);
|
||||
// wait while kitty spawns
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != 1) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
NLog::log("{}Gesture didnt spawn kitty", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
|
|
@ -146,47 +126,19 @@ static bool test() {
|
|||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture up,3"));
|
||||
|
||||
EXPECT(waitForWindowCount(0, "Gesture closed kitty"), true);
|
||||
counter = 0;
|
||||
while (Tests::windowCount() != 0) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
NLog::log("{}Gesture didnt close kitty", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// This test ensures that `movecursortocorner`, which expects
|
||||
// a single-character direction argument, is parsed correctly.
|
||||
Tests::spawnKitty();
|
||||
OK(getFromSocket("/dispatch movecursortocorner 0"));
|
||||
const std::string cursorPos1 = getFromSocket("/cursorpos");
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture left,4"));
|
||||
const std::string cursorPos2 = getFromSocket("/cursorpos");
|
||||
// The cursor should have moved because of the gesture
|
||||
EXPECT(cursorPos1 != cursorPos2, true);
|
||||
|
||||
// Test that `workspace previous` works correctly after a workspace gesture.
|
||||
{
|
||||
OK(getFromSocket("/keyword gestures:workspace_swipe_invert 0"));
|
||||
OK(getFromSocket("/keyword gestures:workspace_swipe_create_new 1"));
|
||||
OK(getFromSocket("/dispatch workspace 3"));
|
||||
|
||||
// Come to workspace 5 from workspace 3: 5 will remember that.
|
||||
OK(getFromSocket("/dispatch workspace 5"));
|
||||
Tests::spawnKitty(); // Keep workspace 5 open
|
||||
|
||||
// Swipe from 1 to 5: 5 shall remember that.
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
OK(getFromSocket("/dispatch plugin:test:alt 1"));
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture right,3"));
|
||||
OK(getFromSocket("/dispatch plugin:test:alt 0"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 5 (5)");
|
||||
|
||||
// Must return to 1 rather than 3
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 1 (1)");
|
||||
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 5 (5)");
|
||||
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
}
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
|
|
|||
|
|
@ -56,82 +56,82 @@ static bool testGetprop() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 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"})");
|
||||
// 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"})");
|
||||
|
||||
// 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]})");
|
||||
// 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]})");
|
||||
|
||||
// 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]})");
|
||||
// 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]})");
|
||||
|
||||
// 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})");
|
||||
// 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_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})");
|
||||
// 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_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})");
|
||||
// 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_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})");
|
||||
// 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_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})");
|
||||
// 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_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})");
|
||||
// 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})");
|
||||
|
||||
// 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"})");
|
||||
// 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"})");
|
||||
|
||||
// bool window properties
|
||||
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})");
|
||||
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})");
|
||||
|
||||
// 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 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})");
|
||||
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})");
|
||||
|
||||
// errors
|
||||
EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animation"), "window not found");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animationstyle"), "window not found");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found");
|
||||
|
||||
// kill all
|
||||
|
|
|
|||
|
|
@ -1,515 +0,0 @@
|
|||
#include <filesystem>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <thread>
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "../shared.hpp"
|
||||
#include "tests.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static int ret = 0;
|
||||
static std::string flagFile = "/tmp/hyprtester-keybinds.txt";
|
||||
|
||||
// Because i don't feel like changing someone elses code.
|
||||
enum eKeyboardModifierIndex : uint8_t {
|
||||
MOD_SHIFT = 1,
|
||||
MOD_CAPS,
|
||||
MOD_CTRL,
|
||||
MOD_ALT,
|
||||
MOD_MOD2,
|
||||
MOD_MOD3,
|
||||
MOD_META,
|
||||
MOD_MOD5
|
||||
};
|
||||
|
||||
static void clearFlag() {
|
||||
std::filesystem::remove(flagFile);
|
||||
}
|
||||
|
||||
static bool checkFlag() {
|
||||
bool exists = std::filesystem::exists(flagFile);
|
||||
clearFlag();
|
||||
return exists;
|
||||
}
|
||||
|
||||
static bool attemptCheckFlag(int attempts, int intervalMs) {
|
||||
for (int i = 0; i < attempts; i++) {
|
||||
if (checkFlag())
|
||||
return true;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string readKittyOutput() {
|
||||
std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all");
|
||||
// chop off shell prompt
|
||||
std::size_t pos = output.rfind("$");
|
||||
if (pos != std::string::npos) {
|
||||
pos += 1;
|
||||
if (pos < output.size())
|
||||
output.erase(0, pos);
|
||||
}
|
||||
// NLog::log("Kitty output: '{}'", output);
|
||||
return output;
|
||||
}
|
||||
|
||||
static void awaitKittyPrompt() {
|
||||
// wait until we see the shell prompt, meaning it's ready for test inputs
|
||||
for (int i = 0; i < 10; i++) {
|
||||
std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all");
|
||||
if (output.rfind("$") == std::string::npos) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
NLog::log("{}Error: timed out waiting for kitty prompt", Colors::RED);
|
||||
}
|
||||
|
||||
static CUniquePointer<CProcess> spawnRemoteControlKitty() {
|
||||
auto kittyProc = Tests::spawnKitty("keybinds_test", {"-o", "allow_remote_control=yes", "--listen-on", "unix:/tmp/hyprtester-kitty.sock", "--config", "NONE", "/bin/sh"});
|
||||
// wait a bit to ensure shell prompt is sent, we are going to read the text after it
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
if (kittyProc)
|
||||
awaitKittyPrompt();
|
||||
return kittyProc;
|
||||
}
|
||||
|
||||
static void testBind() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword bind SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// await flag
|
||||
EXPECT(attemptCheckFlag(20, 50), true);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testBindKey() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword bind ,Y,exec,touch " + flagFile), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29"));
|
||||
// await flag
|
||||
EXPECT(attemptCheckFlag(20, 50), true);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind ,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testLongPress() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// check no flag on short press
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT(checkFlag(), false);
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testKeyLongPress() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword bindo ,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29"));
|
||||
// check no flag on short press
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT(checkFlag(), false);
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind ,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testLongPressRelease() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// check no flag on short press
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT(checkFlag(), false);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testLongPressOnlyKeyRelease() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// check no flag on short press
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT(checkFlag(), false);
|
||||
// release key, keep modifier
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), false);
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testRepeat() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// await flag
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// check that it continues repeating
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testKeyRepeat() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword binde ,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29"));
|
||||
// await flag
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT(checkFlag(), true);
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// check that it continues repeating
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind ,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testRepeatRelease() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// await flag
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT(checkFlag(), true);
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
clearFlag();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), false);
|
||||
// check that it is not repeating
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testRepeatOnlyKeyRelease() {
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// await flag
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), true);
|
||||
// release key, keep modifier
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
clearFlag();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), false);
|
||||
// check that it is not repeating
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
EXPECT(checkFlag(), false);
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static void testShortcutBind() {
|
||||
auto kittyProc = spawnRemoteControlKitty();
|
||||
if (!kittyProc) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok");
|
||||
EXPECT(getFromSocket("/keyword bind SUPER,Y,sendshortcut,,q,"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// release keybind
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
const std::string output = readKittyOutput();
|
||||
EXPECT_COUNT_STRING(output, "y", 0);
|
||||
EXPECT_COUNT_STRING(output, "q", 1);
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testShortcutBindKey() {
|
||||
auto kittyProc = spawnRemoteControlKitty();
|
||||
if (!kittyProc) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok");
|
||||
EXPECT(getFromSocket("/keyword bind ,Y,sendshortcut,,q,"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29"));
|
||||
// release keybind
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
const std::string output = readKittyOutput();
|
||||
EXPECT_COUNT_STRING(output, "y", 0);
|
||||
// disabled: doesn't work in CI
|
||||
// EXPECT_COUNT_STRING(output, "q", 1);
|
||||
EXPECT(getFromSocket("/keyword unbind ,Y"), "ok");
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testShortcutLongPress() {
|
||||
auto kittyProc = spawnRemoteControlKitty();
|
||||
if (!kittyProc) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok");
|
||||
EXPECT(getFromSocket("/keyword bindo SUPER,Y,sendshortcut,,q,"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_rate 10"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
const std::string output = readKittyOutput();
|
||||
int yCount = Tests::countOccurrences(output, "y");
|
||||
// sometimes 1, sometimes 2, not sure why
|
||||
// keybind press sends 1 y immediately
|
||||
// then repeat triggers, sending 1 y
|
||||
// final release stop repeats, and shouldn't send any more
|
||||
EXPECT(true, yCount == 1 || yCount == 2);
|
||||
EXPECT_COUNT_STRING(output, "q", 1);
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testShortcutLongPressKeyRelease() {
|
||||
auto kittyProc = spawnRemoteControlKitty();
|
||||
if (!kittyProc) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok");
|
||||
EXPECT(getFromSocket("/keyword bindo SUPER,Y,sendshortcut,,q,"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_rate 10"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// release key, keep modifier
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||
// await repeat delay
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
const std::string output = readKittyOutput();
|
||||
// disabled: doesn't work on CI
|
||||
// EXPECT_COUNT_STRING(output, "y", 1);
|
||||
EXPECT_COUNT_STRING(output, "q", 0);
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testShortcutRepeat() {
|
||||
auto kittyProc = spawnRemoteControlKitty();
|
||||
if (!kittyProc) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok");
|
||||
EXPECT(getFromSocket("/keyword binde SUPER,Y,sendshortcut,,q,"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_rate 5"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 200"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
// await repeat
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(210));
|
||||
// release keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(450));
|
||||
const std::string output = readKittyOutput();
|
||||
EXPECT_COUNT_STRING(output, "y", 0);
|
||||
int qCount = Tests::countOccurrences(output, "q");
|
||||
// sometimes 2, sometimes 3, not sure why
|
||||
// keybind press sends 1 q immediately
|
||||
// then repeat triggers, sending 1 q
|
||||
// final release stop repeats, and shouldn't send any more
|
||||
EXPECT(true, qCount == 2 || qCount == 3);
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testShortcutRepeatKeyRelease() {
|
||||
auto kittyProc = spawnRemoteControlKitty();
|
||||
if (!kittyProc) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok");
|
||||
EXPECT(getFromSocket("/keyword binde SUPER,Y,sendshortcut,,q,"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_rate 5"), "ok");
|
||||
EXPECT(getFromSocket("/keyword input:repeat_delay 200"), "ok");
|
||||
// press keybind
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(210));
|
||||
// release key, keep modifier
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||
// if repeat was still active, we'd get 2 more q's here
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(450));
|
||||
// release modifier
|
||||
const std::string output = readKittyOutput();
|
||||
EXPECT_COUNT_STRING(output, "y", 0);
|
||||
int qCount = Tests::countOccurrences(output, "q");
|
||||
// sometimes 2, sometimes 3, not sure why
|
||||
// keybind press sends 1 q immediately
|
||||
// then repeat triggers, sending 1 q
|
||||
// final release stop repeats, and shouldn't send any more
|
||||
EXPECT(true, qCount == 2 || qCount == 3);
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testSubmap() {
|
||||
const auto press = [](const uint32_t key, const uint32_t mod = 0) {
|
||||
// +8 because udev -> XKB keycode.
|
||||
getFromSocket("/dispatch plugin:test:keybind 1," + std::to_string(mod) + "," + std::to_string(key + 8));
|
||||
getFromSocket("/dispatch plugin:test:keybind 0," + std::to_string(mod) + "," + std::to_string(key + 8));
|
||||
};
|
||||
|
||||
NLog::log("{}Testing submaps", Colors::GREEN);
|
||||
// submap 1 no resets
|
||||
press(KEY_U, MOD_META);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "submap1");
|
||||
press(KEY_O);
|
||||
Tests::waitUntilWindowsN(1);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "submap1");
|
||||
// submap 2 resets to submap 1
|
||||
press(KEY_U);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "submap2");
|
||||
press(KEY_O);
|
||||
Tests::waitUntilWindowsN(2);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "submap1");
|
||||
// submap 3 resets to default
|
||||
press(KEY_I);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "submap3");
|
||||
press(KEY_O);
|
||||
Tests::waitUntilWindowsN(3);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "default");
|
||||
// submap 1 reset via keybind
|
||||
press(KEY_U, MOD_META);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "submap1");
|
||||
press(KEY_P);
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "default");
|
||||
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static void testSubmapUniversal() {
|
||||
NLog::log("{}Testing submap universal", Colors::GREEN);
|
||||
|
||||
EXPECT(checkFlag(), false);
|
||||
EXPECT(getFromSocket("/keyword bindu SUPER,Y,exec,touch " + flagFile), "ok");
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "default");
|
||||
|
||||
// keybind works on default submap
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||
EXPECT(attemptCheckFlag(30, 5), true);
|
||||
|
||||
// keybind works on submap1
|
||||
getFromSocket("/dispatch plugin:test:keybind 1,7,30");
|
||||
getFromSocket("/dispatch plugin:test:keybind 0,7,30");
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "submap1");
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||
EXPECT(attemptCheckFlag(30, 5), true);
|
||||
|
||||
// reset to default submap
|
||||
getFromSocket("/dispatch plugin:test:keybind 1,0,33");
|
||||
getFromSocket("/dispatch plugin:test:keybind 0,0,33");
|
||||
EXPECT_CONTAINS(getFromSocket("/submap"), "default");
|
||||
|
||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing keybinds", Colors::GREEN);
|
||||
|
||||
clearFlag();
|
||||
|
||||
testBind();
|
||||
testBindKey();
|
||||
testLongPress();
|
||||
testKeyLongPress();
|
||||
testLongPressRelease();
|
||||
testLongPressOnlyKeyRelease();
|
||||
testRepeat();
|
||||
testKeyRepeat();
|
||||
testRepeatRelease();
|
||||
testRepeatOnlyKeyRelease();
|
||||
testShortcutBind();
|
||||
testShortcutBindKey();
|
||||
testShortcutLongPress();
|
||||
testShortcutLongPressKeyRelease();
|
||||
testShortcutRepeat();
|
||||
testShortcutRepeatKeyRelease();
|
||||
testSubmap();
|
||||
testSubmapUniversal();
|
||||
|
||||
clearFlag();
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
||||
|
|
@ -53,7 +53,7 @@ static bool test() {
|
|||
|
||||
NLog::log("{}Testing new_window_takes_over_fullscreen", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
||||
|
||||
Tests::spawnKitty("kitty_A");
|
||||
|
||||
|
|
@ -73,16 +73,7 @@ static bool test() {
|
|||
EXPECT_CONTAINS(str, "kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||
|
||||
{
|
||||
// should be ignored as per focus_under_fullscreen 0
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
EXPECT_CONTAINS(str, "kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 1"));
|
||||
|
||||
Tests::spawnKitty("kitty_C");
|
||||
|
||||
|
|
@ -92,7 +83,7 @@ static bool test() {
|
|||
EXPECT_CONTAINS(str, "kitty_C");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 2"));
|
||||
|
||||
Tests::spawnKitty("kitty_D");
|
||||
|
||||
|
|
@ -102,7 +93,7 @@ static bool test() {
|
|||
EXPECT_CONTAINS(str, "kitty_D");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
|
|
@ -140,74 +131,6 @@ static bool test() {
|
|||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Testing fullscreen and fullscreenstate dispatcher", Colors::YELLOW);
|
||||
|
||||
Tests::spawnKitty("kitty_A");
|
||||
Tests::spawnKitty("kitty_B");
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 0 unset"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 1 toggle"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 1");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 1 toggle"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreenstate 2 2 set"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreenstate 2 2 set"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreenstate 2 2 toggle"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreenstate 2 2 toggle"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
// Ensure that the process autostarted in the config does not
|
||||
// become a zombie even if it terminates very quickly.
|
||||
EXPECT(Tests::execAndGet("pgrep -f 'sleep 0'").empty(), true);
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "../shared.hpp"
|
||||
#include "tests.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static bool testTags() {
|
||||
NLog::log("{}Testing tags", Colors::GREEN);
|
||||
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
NLog::log("{}Spawning kittyProcA&B on ws 1", Colors::YELLOW);
|
||||
auto kittyProcA = Tests::spawnKitty("tagged");
|
||||
auto kittyProcB = Tests::spawnKitty("untagged");
|
||||
|
||||
if (!kittyProcA || !kittyProcB) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Testing testTag tags", Colors::YELLOW);
|
||||
|
||||
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 no_dim 0 & no_shadow", Colors::YELLOW);
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag");
|
||||
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 no_shadow"), "true");
|
||||
EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "false");
|
||||
|
||||
Tests::killAllWindows();
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(testTags)
|
||||
|
|
@ -1,12 +1,8 @@
|
|||
#include <cmath>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/string/VarList2.hpp>
|
||||
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
|
|
@ -15,9 +11,9 @@
|
|||
|
||||
static int ret = 0;
|
||||
|
||||
static bool spawnKitty(const std::string& class_, const std::vector<std::string>& args = {}) {
|
||||
static bool spawnKitty(const std::string& class_) {
|
||||
NLog::log("{}Spawning {}", Colors::YELLOW, class_);
|
||||
if (!Tests::spawnKitty(class_, args)) {
|
||||
if (!Tests::spawnKitty(class_)) {
|
||||
NLog::log("{}Error: {} did not spawn", Colors::RED, class_);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -135,244 +131,6 @@ static void testSwapWindow() {
|
|||
EXPECT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
static void testGroupRules() {
|
||||
NLog::log("{}Testing group window rules", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword general:border_size 8"));
|
||||
OK(getFromSocket("/keyword workspace w[tv1], bordersize:0"));
|
||||
OK(getFromSocket("/keyword workspace f[1], bordersize:0"));
|
||||
OK(getFromSocket("/keyword windowrule match:workspace w[tv1], border_size 0"));
|
||||
OK(getFromSocket("/keyword windowrule match:workspace f[1], border_size 0"));
|
||||
|
||||
if (!Tests::spawnKitty("kitty_A")) {
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/getprop active border_size");
|
||||
EXPECT_CONTAINS(str, "0");
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("kitty_B")) {
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/getprop active border_size");
|
||||
EXPECT_CONTAINS(str, "8");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch togglegroup"));
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||
OK(getFromSocket("/dispatch moveintogroup l"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/getprop active border_size");
|
||||
EXPECT_CONTAINS(str, "0");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch changegroupactive f"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/getprop active border_size");
|
||||
EXPECT_CONTAINS(str, "0");
|
||||
}
|
||||
|
||||
if (!Tests::spawnKitty("kitty_C")) {
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch moveoutofgroup r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/getprop active border_size");
|
||||
EXPECT_CONTAINS(str, "8");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static bool isActiveWindow(const std::string& class_, char fullscreen, bool log = true) {
|
||||
std::string activeWin = getFromSocket("/activewindow");
|
||||
auto winClass = getWindowAttribute(activeWin, "class:");
|
||||
auto winFullscreen = getWindowAttribute(activeWin, "fullscreen:").back();
|
||||
if (winClass.substr(strlen("class: ")) == class_ && winFullscreen == fullscreen)
|
||||
return true;
|
||||
else {
|
||||
if (log)
|
||||
NLog::log("{}Wrong active window: expected class {} fullscreen '{}', found class {}, fullscreen '{}'", Colors::RED, class_, fullscreen, winClass, winFullscreen);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool waitForActiveWindow(const std::string& class_, char fullscreen, int maxTries = 50) {
|
||||
int cnt = 0;
|
||||
while (!isActiveWindow(class_, fullscreen, false)) {
|
||||
++cnt;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
if (cnt > maxTries) {
|
||||
return isActiveWindow(class_, fullscreen, true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Tests behavior of a window being focused when on that window's workspace
|
||||
/// another fullscreen window exists.
|
||||
static bool testWindowFocusOnFullscreenConflict() {
|
||||
if (!spawnKitty("kitty_A"))
|
||||
return false;
|
||||
if (!spawnKitty("kitty_B"))
|
||||
return false;
|
||||
|
||||
OK(getFromSocket("/keyword misc:focus_on_activate true"));
|
||||
|
||||
auto spawnKittyActivating = [] -> std::string {
|
||||
// `XXXXXX` is what `mkstemp` expects to find in the string
|
||||
std::string tmpFilename = (std::filesystem::temp_directory_path() / "XXXXXX").string();
|
||||
int fd = mkstemp(tmpFilename.data());
|
||||
if (fd < 0) {
|
||||
NLog::log("{}Error: could not create tmp file: errno {}", Colors::RED, errno);
|
||||
return "";
|
||||
}
|
||||
(void)close(fd);
|
||||
bool ok = spawnKitty("kitty_activating",
|
||||
{"-o", "allow_remote_control=yes", "--", "/bin/sh", "-c", "while [ -f \"" + tmpFilename + "\" ]; do :; done; kitten @ focus-window; sleep infinity"});
|
||||
if (!ok) {
|
||||
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
||||
return "";
|
||||
}
|
||||
return tmpFilename;
|
||||
};
|
||||
|
||||
// Unfullscreen on conflict
|
||||
{
|
||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
|
||||
// Dispatch-focus the same window
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
|
||||
// Dispatch-focus a different window
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||
EXPECT(isActiveWindow("kitty_B", '0'), true);
|
||||
|
||||
// Make a window that will request focus
|
||||
const std::string removeToActivate = spawnKittyActivating();
|
||||
if (removeToActivate.empty())
|
||||
return false;
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
std::filesystem::remove(removeToActivate);
|
||||
EXPECT(waitForActiveWindow("kitty_activating", '0'), true);
|
||||
OK(getFromSocket("/dispatch forcekillactive"));
|
||||
Tests::waitUntilWindowsN(2);
|
||||
}
|
||||
|
||||
// Take over on conflict
|
||||
{
|
||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
|
||||
// Dispatch-focus the same window
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
|
||||
// Dispatch-focus a different window
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||
EXPECT(isActiveWindow("kitty_B", '2'), true);
|
||||
OK(getFromSocket("/dispatch fullscreenstate 0 0"));
|
||||
|
||||
// Make a window that will request focus
|
||||
const std::string removeToActivate = spawnKittyActivating();
|
||||
if (removeToActivate.empty())
|
||||
return false;
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
std::filesystem::remove(removeToActivate);
|
||||
EXPECT(waitForActiveWindow("kitty_activating", '2'), true);
|
||||
OK(getFromSocket("/dispatch forcekillactive"));
|
||||
Tests::waitUntilWindowsN(2);
|
||||
}
|
||||
|
||||
// Keep the old focus on conflict
|
||||
{
|
||||
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
|
||||
// Dispatch-focus the same window
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
|
||||
// Make a window that will request focus - the setting is treated normally
|
||||
const std::string removeToActivate = spawnKittyActivating();
|
||||
if (removeToActivate.empty())
|
||||
return false;
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||
std::filesystem::remove(removeToActivate);
|
||||
EXPECT(waitForActiveWindow("kitty_A", '2'), true);
|
||||
}
|
||||
|
||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void testMaximizeSize() {
|
||||
NLog::log("{}Testing maximize size", Colors::GREEN);
|
||||
|
||||
EXPECT(spawnKitty("kitty_A"), true);
|
||||
|
||||
// check kitty properties. Maximizing shouldnt change its size
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT(str.contains("at: 22,22"), true);
|
||||
EXPECT(str.contains("size: 1876,1036"), true);
|
||||
EXPECT(str.contains("fullscreen: 0"), true);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT(str.contains("at: 22,22"), true);
|
||||
EXPECT(str.contains("size: 1876,1036"), true);
|
||||
EXPECT(str.contains("fullscreen: 1"), true);
|
||||
}
|
||||
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing windows", Colors::GREEN);
|
||||
|
||||
|
|
@ -394,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::floor(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::floor(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"));
|
||||
|
|
@ -439,38 +179,12 @@ static bool test() {
|
|||
if (!spawnKitty("kitty_B"))
|
||||
return false;
|
||||
|
||||
try {
|
||||
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));
|
||||
|
||||
{
|
||||
auto data = getFromSocket("/activewindow");
|
||||
data = data.substr(data.find("size:") + 5);
|
||||
data = data.substr(0, data.find('\n'));
|
||||
|
||||
Hyprutils::String::CVarList2 sizes(std::move(data), 0, ',');
|
||||
|
||||
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH2, 2);
|
||||
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT);
|
||||
|
||||
{
|
||||
auto data = getFromSocket("/activewindow");
|
||||
data = data.substr(data.find("size:") + 5);
|
||||
data = data.substr(0, data.find('\n'));
|
||||
|
||||
Hyprutils::String::CVarList2 sizes(std::move(data), 0, ',');
|
||||
|
||||
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH_A_FINAL, 2);
|
||||
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2);
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
NLog::log("{}Exception thrown", Colors::RED);
|
||||
EXPECT(false, true);
|
||||
}
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
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"));
|
||||
}
|
||||
|
|
@ -480,7 +194,16 @@ static bool test() {
|
|||
getFromSocket("/dispatch exec xeyes");
|
||||
|
||||
NLog::log("{}Keep checking if xeyes spawned", Colors::YELLOW);
|
||||
Tests::waitUntilWindowsN(3);
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != 3) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
EXPECT(Tests::windowCount(), 3);
|
||||
return !ret;
|
||||
}
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 3 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 3);
|
||||
|
|
@ -505,255 +228,6 @@ static bool test() {
|
|||
|
||||
testSwapWindow();
|
||||
|
||||
getFromSocket("/dispatch workspace 1");
|
||||
|
||||
if (!testWindowFocusOnFullscreenConflict()) {
|
||||
ret = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Testing spawning a floating window over a fullscreen window", Colors::YELLOW);
|
||||
{
|
||||
if (!spawnKitty("kitty_A"))
|
||||
return false;
|
||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
OK(getFromSocket("/dispatch exec [float] kitty"));
|
||||
Tests::waitUntilWindowsN(2);
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:^kitty$"));
|
||||
const auto focused1 = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(focused1, "class: kitty\n");
|
||||
|
||||
OK(getFromSocket("/dispatch killwindow activewindow"));
|
||||
Tests::waitUntilWindowsN(1);
|
||||
|
||||
// The old window should be focused again
|
||||
const auto focused2 = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(focused2, "class: kitty_A\n");
|
||||
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
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[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;
|
||||
|
||||
auto dwindle = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(dwindle, "size: 1500,500");
|
||||
EXPECT_CONTAINS(dwindle, "at: 210,290");
|
||||
|
||||
if (!spawnKitty("kitty_maxsize"))
|
||||
return false;
|
||||
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500");
|
||||
|
||||
Tests::killAllWindows();
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
OK(getFromSocket("/keyword general:layout master"));
|
||||
|
||||
if (!spawnKitty("kitty_maxsize"))
|
||||
return false;
|
||||
|
||||
auto master = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(master, "size: 1500,500");
|
||||
EXPECT_CONTAINS(master, "at: 210,290");
|
||||
|
||||
if (!spawnKitty("kitty_maxsize"))
|
||||
return false;
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_maxsize"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500")
|
||||
|
||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||
OK(getFromSocket("/reload"));
|
||||
Tests::killAllWindows();
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
}
|
||||
|
||||
NLog::log("{}Testing window rules", Colors::YELLOW);
|
||||
if (!spawnKitty("wr_kitty"))
|
||||
return false;
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
const int SIZE = 200;
|
||||
EXPECT_CONTAINS(str, "floating: 1");
|
||||
EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE));
|
||||
EXPECT_NOT_CONTAINS(str, "pinned: 1");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// test persistent_size between floating window launches
|
||||
OK(getFromSocket("/keyword windowrule match:class persistent_size_kitty, persistent_size true, float true"));
|
||||
|
||||
if (!spawnKitty("persistent_size_kitty"))
|
||||
return false;
|
||||
|
||||
OK(getFromSocket("/dispatch resizeactive exact 600 400"))
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "size: 600,400");
|
||||
EXPECT_CONTAINS(str, "floating: 1");
|
||||
}
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
if (!spawnKitty("persistent_size_kitty"))
|
||||
return false;
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "size: 600,400");
|
||||
EXPECT_CONTAINS(str, "floating: 1");
|
||||
}
|
||||
|
||||
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"));
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
Tests::killAllWindows();
|
||||
|
||||
testGroupRules();
|
||||
|
||||
testMaximizeSize();
|
||||
|
||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
|
@ -15,99 +14,10 @@ static int ret = 0;
|
|||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static bool testAsymmetricGaps() {
|
||||
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();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing workspaces", Colors::GREEN);
|
||||
|
||||
|
|
@ -115,17 +25,7 @@ static bool test() {
|
|||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace 1");
|
||||
|
||||
NLog::log("{}Checking persistent no-mon", Colors::YELLOW);
|
||||
OK(getFromSocket("r/keyword workspace 966,persistent:1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "workspace ID 966 (966)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Spawning kittyProc on ws 1", Colors::YELLOW);
|
||||
auto kittyProcA = Tests::spawnKitty();
|
||||
|
|
@ -449,8 +349,6 @@ static bool test() {
|
|||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
testAsymmetricGaps();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,15 +9,10 @@
|
|||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_, const std::vector<std::string> args) {
|
||||
CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_) {
|
||||
const auto COUNT_BEFORE = windowCount();
|
||||
|
||||
std::vector<std::string> programArgs = args;
|
||||
if (!class_.empty()) {
|
||||
programArgs.insert(programArgs.begin(), "--class");
|
||||
programArgs.insert(programArgs.begin() + 1, class_);
|
||||
}
|
||||
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("kitty", programArgs);
|
||||
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("kitty", class_.empty() ? std::vector<std::string>{} : std::vector<std::string>{"--class", class_});
|
||||
kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
||||
kitty->runAsync();
|
||||
|
||||
|
|
@ -54,7 +49,7 @@ int Tests::countOccurrences(const std::string& in, const std::string& what) {
|
|||
auto pos = in.find(what);
|
||||
while (pos != std::string::npos) {
|
||||
cnt++;
|
||||
pos = in.find(what, pos + what.length());
|
||||
pos = in.find(what, pos + what.length() - 1);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
|
|
@ -95,13 +90,3 @@ void Tests::waitUntilWindowsN(int n) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Tests::execAndGet(const std::string& cmd) {
|
||||
CProcess proc("/bin/sh", {"-c", cmd});
|
||||
|
||||
if (!proc.runSync()) {
|
||||
return "error";
|
||||
}
|
||||
|
||||
return proc.stdOut();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,10 @@
|
|||
|
||||
//NOLINTNEXTLINE
|
||||
namespace Tests {
|
||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "", const std::vector<std::string> args = {});
|
||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "");
|
||||
bool processAlive(pid_t pid);
|
||||
int windowCount();
|
||||
int countOccurrences(const std::string& in, const std::string& what);
|
||||
bool killAllWindows();
|
||||
void waitUntilWindowsN(int n);
|
||||
std::string execAndGet(const std::string& cmd);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ $menu = wofi --show drun
|
|||
# Autostart necessary processes (like notifications daemons, status bars, etc.)
|
||||
# Or execute your favorite apps at launch like this:
|
||||
|
||||
exec-once = sleep 0 # Terminates very quickly
|
||||
# exec-once = $terminal
|
||||
# exec-once = nm-applet &
|
||||
# exec-once = waybar & hyprpaper & firefox
|
||||
|
|
@ -297,91 +296,32 @@ bindl = , XF86AudioPause, exec, playerctl play-pause
|
|||
bindl = , XF86AudioPlay, exec, playerctl play-pause
|
||||
bindl = , XF86AudioPrev, exec, playerctl previous
|
||||
|
||||
bind = $mainMod, u, submap, submap1
|
||||
|
||||
submap = submap1
|
||||
bind = , u, submap, submap2
|
||||
bind = , i, submap, submap3
|
||||
bind = , o, exec, $terminal
|
||||
bind = , p, submap, reset
|
||||
|
||||
submap = submap2, submap1
|
||||
bind = , o, exec, $terminal
|
||||
|
||||
submap = submap3, reset
|
||||
bind = , o, exec, $terminal
|
||||
|
||||
submap = reset
|
||||
|
||||
|
||||
##############################
|
||||
### WINDOWS AND WORKSPACES ###
|
||||
##############################
|
||||
|
||||
windowrule {
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
name = suppress-maximize-events
|
||||
match:class = .*
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
suppress_event = maximize
|
||||
}
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(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
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
no_focus = true
|
||||
}
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrulev2 = suppressevent maximize, class:.*
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
}
|
||||
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]
|
||||
|
||||
gesture = 3, left, dispatcher, exec, kitty
|
||||
gesture = 3, right, float
|
||||
|
|
@ -392,9 +332,3 @@ gesture = 3, down, mod:ALT, float
|
|||
|
||||
gesture = 3, horizontal, mod:ALT, workspace
|
||||
|
||||
gesture = 5, up, dispatcher, sendshortcut, , e, activewindow
|
||||
gesture = 5, down, dispatcher, sendshortcut, , x, activewindow
|
||||
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
|
||||
|
|
|
|||
125
meson.build
Normal file
125
meson.build
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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',
|
||||
'cpp_std=c++26',
|
||||
],
|
||||
)
|
||||
|
||||
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.3')
|
||||
hyprlang = dependency('hyprlang', version: '>= 0.3.2')
|
||||
hyprutils = dependency('hyprutils', version: '>= 0.8.2')
|
||||
aquamarine_version_list = aquamarine.version().split('.')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION="@0@"'.format(aquamarine.version())], language: 'cpp')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION_MAJOR=@0@'.format(aquamarine_version_list.get(0))], language: 'cpp')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION_MINOR=@0@'.format(aquamarine_version_list.get(1))], language: 'cpp')
|
||||
add_project_arguments(['-DAQUAMARINE_VERSION_PATCH=@0@'.format(aquamarine_version_list.get(2))], language: 'cpp')
|
||||
add_project_arguments(['-DHYPRCURSOR_VERSION="@0@"'.format(hyprcursor.version())], language: 'cpp')
|
||||
add_project_arguments(['-DHYPRGRAPHICS_VERSION="@0@"'.format(hyprgraphics.version())], language: 'cpp')
|
||||
add_project_arguments(['-DHYPRLANG_VERSION="@0@"'.format(hyprlang.version())], language: 'cpp')
|
||||
add_project_arguments(['-DHYPRUTILS_VERSION="@0@"'.format(hyprutils.version())], language: 'cpp')
|
||||
|
||||
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) # timerfd on BSDs
|
||||
inotify_dep = dependency('libinotify', required: false) # inotify on BSDs
|
||||
|
||||
re2 = dependency('re2', required: true)
|
||||
|
||||
# Handle options
|
||||
systemd_option = get_option('systemd')
|
||||
systemd = dependency('systemd', required: systemd_option)
|
||||
systemd_option.enable_auto_if(systemd.found())
|
||||
|
||||
if (systemd_option.enabled())
|
||||
message('Enabling systemd integration')
|
||||
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
|
||||
subdir('systemd')
|
||||
endif
|
||||
|
||||
if get_option('buildtype') == 'debug'
|
||||
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
|
||||
endif
|
||||
|
||||
# Generate hyprland version and populate version.h
|
||||
run_command('sh', '-c', 'scripts/generateVersion.sh', check: true)
|
||||
# Make shader files includable
|
||||
run_command('sh', '-c', 'scripts/generateShaderIncludes.sh', check: true)
|
||||
|
||||
# Install headers
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# Generate hyprland.pc
|
||||
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'],
|
||||
)
|
||||
5
meson_options.txt
Normal file
5
meson_options.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
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')
|
||||
|
|
@ -6,21 +6,21 @@
|
|||
pkgconf,
|
||||
makeWrapper,
|
||||
cmake,
|
||||
meson,
|
||||
ninja,
|
||||
aquamarine,
|
||||
binutils,
|
||||
cairo,
|
||||
epoll-shim,
|
||||
git,
|
||||
glaze-hyprland,
|
||||
gtest,
|
||||
glaze,
|
||||
hyprcursor,
|
||||
hyprgraphics,
|
||||
hyprland-protocols,
|
||||
hyprland-guiutils,
|
||||
hyprland-qtutils,
|
||||
hyprlang,
|
||||
hyprutils,
|
||||
hyprwayland-scanner,
|
||||
hyprwire,
|
||||
libGL,
|
||||
libdrm,
|
||||
libexecinfo,
|
||||
|
|
@ -28,7 +28,6 @@
|
|||
libxkbcommon,
|
||||
libuuid,
|
||||
libgbm,
|
||||
muparser,
|
||||
pango,
|
||||
pciutils,
|
||||
re2,
|
||||
|
|
@ -41,7 +40,6 @@
|
|||
xorg,
|
||||
xwayland,
|
||||
debug ? false,
|
||||
withTests ? false,
|
||||
enableXWayland ? true,
|
||||
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
|
||||
wrapRuntimeDeps ? true,
|
||||
|
|
@ -54,13 +52,12 @@
|
|||
nvidiaPatches ? false,
|
||||
hidpiXWayland ? false,
|
||||
legacyRenderer ? false,
|
||||
withHyprtester ? false,
|
||||
}: let
|
||||
inherit (builtins) foldl' readFile;
|
||||
inherit (lib.asserts) assertMsg;
|
||||
inherit (lib.attrsets) mapAttrsToList;
|
||||
inherit (lib.lists) flatten concatLists optional optionals;
|
||||
inherit (lib.strings) makeBinPath optionalString cmakeBool trim;
|
||||
inherit (lib.strings) makeBinPath optionalString mesonBool mesonEnable trim;
|
||||
fs = lib.fileset;
|
||||
|
||||
adapters = flatten [
|
||||
|
|
@ -74,10 +71,9 @@ 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 withTests;
|
||||
inherit version;
|
||||
|
||||
src = fs.toSource {
|
||||
root = ../.;
|
||||
|
|
@ -85,23 +81,22 @@ in
|
|||
fs.intersection
|
||||
# allows non-flake builds to only include files tracked by git
|
||||
(fs.gitTracked ../.)
|
||||
(fs.unions (flatten [
|
||||
(fs.unions [
|
||||
../assets/hyprland-portals.conf
|
||||
../assets/install
|
||||
../hyprctl
|
||||
../hyprland.pc.in
|
||||
../LICENSE
|
||||
../meson_options.txt
|
||||
../protocols
|
||||
../src
|
||||
../start
|
||||
../systemd
|
||||
../VERSION
|
||||
(fs.fileFilter (file: file.hasExt "1") ../docs)
|
||||
(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 withTests [../tests ../hyprtester])
|
||||
]));
|
||||
(fs.fileFilter (file: file.name == "meson.build") ../.)
|
||||
]);
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
|
|
@ -112,13 +107,11 @@ in
|
|||
sed -i "s#@PREFIX@/##g" hyprland.pc.in
|
||||
'';
|
||||
|
||||
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")}";
|
||||
};
|
||||
COMMITS = revCount;
|
||||
DATE = date;
|
||||
DIRTY = optionalString (commit == "") "dirty";
|
||||
HASH = commit;
|
||||
TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
|
||||
|
||||
depsBuildBuild = [
|
||||
pkg-config
|
||||
|
|
@ -126,9 +119,10 @@ in
|
|||
|
||||
nativeBuildInputs = [
|
||||
hyprwayland-scanner
|
||||
hyprwire
|
||||
makeWrapper
|
||||
cmake
|
||||
meson
|
||||
ninja
|
||||
cmake # needed for glaze
|
||||
pkg-config
|
||||
];
|
||||
|
||||
|
|
@ -143,21 +137,18 @@ in
|
|||
aquamarine
|
||||
cairo
|
||||
git
|
||||
glaze-hyprland
|
||||
gtest
|
||||
glaze
|
||||
hyprcursor
|
||||
hyprgraphics
|
||||
hyprland-protocols
|
||||
hyprlang
|
||||
hyprutils
|
||||
hyprwire
|
||||
libdrm
|
||||
libGL
|
||||
libinput
|
||||
libuuid
|
||||
libxkbcommon
|
||||
libgbm
|
||||
muparser
|
||||
pango
|
||||
pciutils
|
||||
re2
|
||||
|
|
@ -183,49 +174,34 @@ in
|
|||
|
||||
strictDeps = true;
|
||||
|
||||
cmakeBuildType =
|
||||
mesonBuildType =
|
||||
if debug
|
||||
then "Debug"
|
||||
else "RelWithDebInfo";
|
||||
then "debug"
|
||||
else "release";
|
||||
|
||||
# we want as much debug info as possible
|
||||
dontStrip = debug;
|
||||
|
||||
cmakeFlags = mapAttrsToList cmakeBool {
|
||||
"BUILT_WITH_NIX" = true;
|
||||
"NO_XWAYLAND" = !enableXWayland;
|
||||
"LEGACY_RENDERER" = legacyRenderer;
|
||||
"NO_SYSTEMD" = !withSystemd;
|
||||
"CMAKE_DISABLE_PRECOMPILE_HEADERS" = true;
|
||||
"NO_UWSM" = true;
|
||||
"NO_HYPRPM" = true;
|
||||
"TRACY_ENABLE" = false;
|
||||
"WITH_TESTS" = withTests;
|
||||
};
|
||||
|
||||
preConfigure = ''
|
||||
substituteInPlace hyprtester/CMakeLists.txt --replace-fail \
|
||||
"\''${CMAKE_CURRENT_BINARY_DIR}" \
|
||||
"${placeholder "out"}/bin"
|
||||
'';
|
||||
mesonFlags = flatten [
|
||||
(mapAttrsToList mesonEnable {
|
||||
"xwayland" = enableXWayland;
|
||||
"systemd" = withSystemd;
|
||||
"uwsm" = false;
|
||||
"hyprpm" = false;
|
||||
})
|
||||
(mapAttrsToList mesonBool {
|
||||
"b_pch" = false;
|
||||
"tracy_enable" = false;
|
||||
})
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
${optionalString wrapRuntimeDeps ''
|
||||
wrapProgram $out/bin/Hyprland \
|
||||
--suffix PATH : ${makeBinPath [
|
||||
binutils
|
||||
hyprland-guiutils
|
||||
hyprland-qtutils
|
||||
pciutils
|
||||
pkgconf
|
||||
]}
|
||||
''}
|
||||
|
||||
${optionalString withTests ''
|
||||
install hyprtester/pointer-warp -t $out/bin
|
||||
install hyprtester/pointer-scroll -t $out/bin
|
||||
install hyprland_gtests -t $out/bin
|
||||
install hyprtester/child-window -t $out/bin
|
||||
''}
|
||||
'';
|
||||
|
||||
passthru.providedSessions = ["hyprland"];
|
||||
|
|
|
|||
69
nix/hyprtester.nix
Normal file
69
nix/hyprtester.nix
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
stdenvAdapters,
|
||||
cmake,
|
||||
pkg-config,
|
||||
hyprland,
|
||||
hyprwayland-scanner,
|
||||
version ? "git",
|
||||
}: let
|
||||
inherit (lib.lists) flatten foldl';
|
||||
inherit (lib.sources) cleanSourceWith cleanSource;
|
||||
inherit (lib.strings) hasSuffix cmakeBool;
|
||||
|
||||
adapters = flatten [
|
||||
stdenvAdapters.useMoldLinker
|
||||
stdenvAdapters.keepDebugInfo
|
||||
];
|
||||
|
||||
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
||||
in
|
||||
customStdenv.mkDerivation (finalAttrs: {
|
||||
pname = "hyprtester";
|
||||
inherit version;
|
||||
|
||||
src = cleanSourceWith {
|
||||
filter = name: _type: let
|
||||
baseName = baseNameOf (toString name);
|
||||
in
|
||||
! (hasSuffix ".nix" baseName);
|
||||
src = cleanSource ../.;
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
pkg-config
|
||||
hyprwayland-scanner
|
||||
];
|
||||
|
||||
buildInputs = hyprland.buildInputs;
|
||||
|
||||
preConfigure = ''
|
||||
substituteInPlace hyprtester/CMakeLists.txt --replace-fail \
|
||||
"\''${CMAKE_CURRENT_BINARY_DIR}" \
|
||||
"${placeholder "out"}/bin"
|
||||
|
||||
cmake -S . -B .
|
||||
cmake --build . --target generate-protocol-headers -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
cd hyprtester
|
||||
'';
|
||||
|
||||
postInstall = ''
|
||||
install pointer-warp -t $out/bin
|
||||
install pointer-scroll -t $out/bin
|
||||
'';
|
||||
|
||||
cmakeBuildType = "Debug";
|
||||
|
||||
cmakeFlags = [(cmakeBool "TESTS" true)];
|
||||
|
||||
meta = {
|
||||
homepage = "https://github.com/hyprwm/Hyprland";
|
||||
description = "Hyprland testing framework";
|
||||
license = lib.licenses.bsd3;
|
||||
platforms = hyprland.meta.platforms;
|
||||
mainProgram = "hyprtester";
|
||||
};
|
||||
})
|
||||
|
|
@ -24,13 +24,11 @@ in {
|
|||
inputs.hyprcursor.overlays.default
|
||||
inputs.hyprgraphics.overlays.default
|
||||
inputs.hyprland-protocols.overlays.default
|
||||
inputs.hyprland-guiutils.overlays.default
|
||||
inputs.hyprland-qtutils.overlays.default
|
||||
inputs.hyprlang.overlays.default
|
||||
inputs.hyprutils.overlays.default
|
||||
inputs.hyprwayland-scanner.overlays.default
|
||||
inputs.hyprwire.overlays.default
|
||||
self.overlays.udis86
|
||||
self.overlays.glaze
|
||||
|
||||
# Hyprland packages themselves
|
||||
(final: _prev: let
|
||||
|
|
@ -45,14 +43,9 @@ in {
|
|||
};
|
||||
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
|
||||
|
||||
hyprland-with-tests = final.hyprland.override {withTests = true;};
|
||||
|
||||
hyprland-with-hyprtester =
|
||||
builtins.trace ''
|
||||
hyprland-with-hyprtester was removed. Please use the hyprland package.
|
||||
Hyprtester is always built now.
|
||||
''
|
||||
final.hyprland;
|
||||
hyprtester = final.callPackage ./hyprtester.nix {
|
||||
inherit version;
|
||||
};
|
||||
|
||||
# deprecated packages
|
||||
hyprland-legacy-renderer =
|
||||
|
|
@ -111,13 +104,4 @@ in {
|
|||
patches = [];
|
||||
});
|
||||
};
|
||||
|
||||
# Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true.
|
||||
# Since we don't include openssl, the build failes without the `enableSSL = false;` override
|
||||
glaze = final: prev: {
|
||||
glaze-hyprland = prev.glaze.override {
|
||||
enableSSL = false;
|
||||
enableInterop = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
inputs: pkgs: let
|
||||
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||
hyprland = flake.hyprland-with-tests;
|
||||
hyprland = flake.hyprland;
|
||||
in {
|
||||
tests = pkgs.testers.runNixOSTest {
|
||||
name = "hyprland-tests";
|
||||
|
||||
nodes.machine = {pkgs, ...}: {
|
||||
environment.systemPackages = with pkgs; [
|
||||
flake.hyprtester
|
||||
|
||||
# Programs needed for tests
|
||||
jq
|
||||
kitty
|
||||
wl-clipboard
|
||||
xorg.xeyes
|
||||
];
|
||||
|
||||
|
|
@ -36,7 +37,7 @@ in {
|
|||
};
|
||||
|
||||
# Test configuration
|
||||
environment.etc."test.conf".source = "${hyprland}/share/hypr/test.conf";
|
||||
environment.etc."test.conf".source = "${flake.hyprtester}/share/hypr/test.conf";
|
||||
|
||||
# Disable portals
|
||||
xdg.portal.enable = pkgs.lib.mkForce false;
|
||||
|
|
@ -68,21 +69,14 @@ in {
|
|||
# Wait for tty to be up
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
|
||||
# Run gtests
|
||||
print("Running gtests")
|
||||
exit_status, _out = machine.execute("su - alice -c 'hyprland_gtests 2>&1 | tee /tmp/gtestslog; exit ''${PIPESTATUS[0]}'")
|
||||
machine.execute(f'echo {exit_status} > /tmp/exit_status_gtests')
|
||||
|
||||
# Run hyprtester testing framework/suite
|
||||
print("Running hyprtester")
|
||||
exit_status, _out = machine.execute("su - alice -c 'hyprtester -b ${hyprland}/bin/Hyprland -c /etc/test.conf -p ${hyprland}/lib/hyprtestplugin.so 2>&1 | tee /tmp/testerlog; exit ''${PIPESTATUS[0]}'")
|
||||
exit_status, _out = machine.execute("su - alice -c 'hyprtester -b ${hyprland}/bin/Hyprland -c /etc/test.conf -p ${flake.hyprtester}/lib/hyprtestplugin.so 2>&1 | tee /tmp/testerlog; exit ''${PIPESTATUS[0]}'")
|
||||
print(f"Hyprtester exited with {exit_status}")
|
||||
|
||||
# Copy logs to host
|
||||
machine.execute('cp "$(find /tmp/hypr -name *.log | head -1)" /tmp/hyprlog')
|
||||
machine.execute(f'echo {exit_status} > /tmp/exit_status')
|
||||
machine.copy_from_vm("/tmp/gtestslog")
|
||||
machine.copy_from_vm("/tmp/testerlog")
|
||||
machine.copy_from_vm("/tmp/hyprlog")
|
||||
machine.copy_from_vm("/tmp/exit_status")
|
||||
|
|
|
|||
117
protocols/meson.build
Normal file
117
protocols/meson.build
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
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',
|
||||
]
|
||||
|
||||
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,
|
||||
)
|
||||
27
scripts/generateVersion.sh
Executable file
27
scripts/generateVersion.sh
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/sh
|
||||
|
||||
# if the git directory doesn't exist, don't gather data to avoid overwriting, unless
|
||||
# the version file is missing altogether (otherwise compiling will fail)
|
||||
if [ ! -d ./.git ]; then
|
||||
if [ -f ./src/version.h ]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
cp -fr ./src/version.h.in ./src/version.h
|
||||
|
||||
HASH=${HASH-$(git rev-parse HEAD)}
|
||||
BRANCH=${BRANCH-$(git branch --show-current)}
|
||||
MESSAGE=${MESSAGE-$(git show | head -n 5 | tail -n 1 | sed -e 's/#//g' -e 's/\"//g')}
|
||||
DATE=${DATE-$(git show --no-patch --format=%cd --date=local)}
|
||||
DIRTY=${DIRTY-$(git diff-index --quiet HEAD -- || echo dirty)}
|
||||
TAG=${TAG-$(git describe --tags)}
|
||||
COMMITS=${COMMITS-$(git rev-list --count HEAD)}
|
||||
|
||||
sed -i -e "s#@HASH@#${HASH}#" ./src/version.h
|
||||
sed -i -e "s#@BRANCH@#${BRANCH}#" ./src/version.h
|
||||
sed -i -e "s#@MESSAGE@#${MESSAGE}#" ./src/version.h
|
||||
sed -i -e "s#@DATE@#${DATE}#" ./src/version.h
|
||||
sed -i -e "s#@DIRTY@#${DIRTY}#" ./src/version.h
|
||||
sed -i -e "s#@TAG@#${TAG}#" ./src/version.h
|
||||
sed -i -e "s#@COMMITS@#${COMMITS}#" ./src/version.h
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,7 @@
|
|||
#include "managers/XWaylandManager.hpp"
|
||||
#include "managers/KeybindManager.hpp"
|
||||
#include "managers/SessionLockManager.hpp"
|
||||
#include "desktop/view/Window.hpp"
|
||||
#include "desktop/Window.hpp"
|
||||
#include "protocols/types/ColorManagement.hpp"
|
||||
|
||||
#include <aquamarine/backend/Backend.hpp>
|
||||
|
|
@ -40,7 +40,6 @@ class CCompositor {
|
|||
} m_drmRenderNode;
|
||||
|
||||
bool m_initialized = false;
|
||||
bool m_safeMode = false;
|
||||
SP<Aquamarine::CBackend> m_aqBackend;
|
||||
|
||||
std::string m_hyprTempDataRoot = "";
|
||||
|
|
@ -56,7 +55,6 @@ class CCompositor {
|
|||
std::vector<PHLLS> m_layers;
|
||||
std::vector<PHLWINDOWREF> m_windowsFadingOut;
|
||||
std::vector<PHLLSREF> m_surfacesFadingOut;
|
||||
std::vector<SP<Desktop::View::IView>> m_otherViews;
|
||||
|
||||
std::unordered_map<std::string, MONITORID> m_monitorIDMap;
|
||||
std::unordered_map<std::string, WORKSPACEID> m_seenMonitorWorkspaceMap; // map of seen monitor names to workspace IDs
|
||||
|
|
@ -67,7 +65,12 @@ class CCompositor {
|
|||
void cleanup();
|
||||
void bumpNofile();
|
||||
void restoreNofile();
|
||||
bool setWatchdogFd(int fd);
|
||||
|
||||
WP<CWLSurfaceResource> m_lastFocus;
|
||||
PHLWINDOWREF m_lastWindow;
|
||||
PHLMONITORREF m_lastMonitor;
|
||||
|
||||
std::vector<PHLWINDOWREF> m_windowFocusHistory; // first element is the most recently focused
|
||||
|
||||
bool m_readyToProcess = false;
|
||||
bool m_sessionActive = true;
|
||||
|
|
@ -96,6 +99,8 @@ class CCompositor {
|
|||
PHLMONITOR getMonitorFromCursor();
|
||||
PHLMONITOR getMonitorFromVector(const Vector2D&);
|
||||
void removeWindowFromVectorSafe(PHLWINDOW);
|
||||
void focusWindow(PHLWINDOW, SP<CWLSurfaceResource> pSurface = nullptr, bool preserveFocusHistory = false);
|
||||
void focusSurface(SP<CWLSurfaceResource>, PHLWINDOW pWindowOwner = nullptr);
|
||||
bool monitorExists(PHLMONITOR);
|
||||
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
|
||||
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*, bool aboveLockscreen = false);
|
||||
|
|
@ -120,10 +125,10 @@ class CCompositor {
|
|||
WORKSPACEID getNextAvailableNamedWorkspace();
|
||||
bool isPointOnAnyMonitor(const Vector2D&);
|
||||
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
|
||||
CBox calculateX11WorkArea();
|
||||
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);
|
||||
|
|
@ -131,7 +136,7 @@ class CCompositor {
|
|||
bool workspaceIDOutOfBounds(const WORKSPACEID&);
|
||||
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const Desktop::View::SFullscreenState state);
|
||||
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const SFullscreenState state);
|
||||
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
|
||||
PHLWINDOW getX11Parent(PHLWINDOW);
|
||||
void scheduleFrameForMonitor(PHLMONITOR, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
|
||||
|
|
@ -145,14 +150,13 @@ class CCompositor {
|
|||
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
|
||||
[[nodiscard]] PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, const std::string& name = "",
|
||||
bool isEmpty = true); // will be deleted next frame if left empty and unfocused!
|
||||
void setActiveMonitor(PHLMONITOR);
|
||||
bool isWorkspaceSpecial(const WORKSPACEID&);
|
||||
WORKSPACEID getNewSpecialID();
|
||||
void performUserChecks();
|
||||
void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace);
|
||||
PHLWINDOW getForceFocus();
|
||||
void scheduleMonitorStateRecheck();
|
||||
void arrangeMonitors();
|
||||
void checkMonitorOverlaps();
|
||||
void enterUnsafeState();
|
||||
void leaveUnsafeState();
|
||||
void setPreferredScaleForSurface(SP<CWLSurfaceResource> pSurface, double scale);
|
||||
|
|
@ -163,30 +167,27 @@ class CCompositor {
|
|||
std::optional<unsigned int> getVTNr();
|
||||
|
||||
NColorManagement::SImageDescription getPreferredImageDescription();
|
||||
NColorManagement::SImageDescription getHDRImageDescription();
|
||||
bool shouldChangePreferredImageDescription();
|
||||
|
||||
bool supportsDrmSyncobjTimeline() const;
|
||||
std::string m_explicitConfigPath;
|
||||
|
||||
private:
|
||||
void initAllSignals();
|
||||
void removeAllSignals();
|
||||
void cleanEnvironment();
|
||||
void setRandomSplash();
|
||||
void initManagers(eManagersInitStage stage);
|
||||
void prepareFallbackOutput();
|
||||
void createLockFile();
|
||||
void removeLockFile();
|
||||
void setMallocThreshold();
|
||||
void openSafeModeBox();
|
||||
void initAllSignals();
|
||||
void removeAllSignals();
|
||||
void cleanEnvironment();
|
||||
void setRandomSplash();
|
||||
void initManagers(eManagersInitStage stage);
|
||||
void prepareFallbackOutput();
|
||||
void createLockFile();
|
||||
void removeLockFile();
|
||||
void setMallocThreshold();
|
||||
|
||||
uint64_t m_hyprlandPID = 0;
|
||||
wl_event_source* m_critSigSource = nullptr;
|
||||
rlimit m_originalNofile = {};
|
||||
Hyprutils::OS::CFileDescriptor m_watchdogWriteFd;
|
||||
uint64_t m_hyprlandPID = 0;
|
||||
wl_event_source* m_critSigSource = nullptr;
|
||||
rlimit m_originalNofile = {};
|
||||
|
||||
std::vector<PHLWORKSPACEREF> m_workspaces;
|
||||
std::vector<PHLWORKSPACEREF> m_workspaces;
|
||||
};
|
||||
|
||||
inline UP<CCompositor> g_pCompositor;
|
||||
|
|
|
|||
|
|
@ -97,29 +97,27 @@ class CCssGapData : public ICustomConfigValueData {
|
|||
int64_t m_bottom;
|
||||
int64_t m_left;
|
||||
|
||||
void parseGapData(CVarList2 varlist) {
|
||||
const auto toInt = [](std::string_view string) -> int { return std::stoi(std::string(string)); };
|
||||
|
||||
void parseGapData(CVarList varlist) {
|
||||
switch (varlist.size()) {
|
||||
case 1: {
|
||||
*this = CCssGapData(toInt(varlist[0]));
|
||||
*this = CCssGapData(std::stoi(varlist[0]));
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]));
|
||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]));
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]));
|
||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]));
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]), toInt(varlist[3]));
|
||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3]));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
Log::logger->log(Log::WARN, "Too many arguments provided for gaps.");
|
||||
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]), toInt(varlist[3]));
|
||||
Debug::log(WARN, "Too many arguments provided for gaps.");
|
||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ inline static const std::vector<SConfigOptionDescription> 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)",
|
||||
|
|
@ -136,18 +142,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:modal_parent_blocking",
|
||||
.description = "if true, parent windows of modals will not be interactive.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "general:locale",
|
||||
.description = "overrides the system locale",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{""},
|
||||
},
|
||||
|
||||
/*
|
||||
* decoration:
|
||||
|
|
@ -435,7 +429,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:kb_file",
|
||||
.description = "Appropriate XKB keymap file",
|
||||
.description = "Appropriate XKB keymap parameter",
|
||||
.type = CONFIG_OPTION_STRING_LONG,
|
||||
.data = SConfigOptionDescription::SStringData{""}, //##TODO UNSET?
|
||||
},
|
||||
|
|
@ -484,12 +478,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:rotation",
|
||||
.description = "Sets the rotation of a device in degrees clockwise off the logical neutral position. Value is clamped to the range 0 to 359.",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 359},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:left_handed",
|
||||
.description = "Switches RMB and LMB",
|
||||
|
|
@ -695,9 +683,9 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
|
||||
SConfigOptionDescription{
|
||||
.value = "input:virtualkeyboard:share_states",
|
||||
.description = "Unify key down states and modifier states with other keyboards. 0 -> no, 1 -> yes, 2 -> yes unless IME client",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||
.description = "Unify key down states and modifier states with other keyboards",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "input:virtualkeyboard:release_pressed_on_close",
|
||||
|
|
@ -1115,12 +1103,6 @@ inline static const std::vector<SConfigOptionDescription> 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:
|
||||
|
|
@ -1279,11 +1261,11 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:on_focus_under_fullscreen",
|
||||
.description = "if there is a fullscreen or maximized window, decide whether a tiled window requested to focus should replace it, stay behind or disable the "
|
||||
"fullscreen/maximized state. 0 - ignore focus request (keep focus on fullscreen window), 1 - takes over, 2 - unfullscreen/unmaximize [0/1/2]",
|
||||
.value = "misc:new_window_takes_over_fullscreen",
|
||||
.description = "if there is a fullscreen or maximized window, decide whether a new tiled window opened should replace it, stay behind or disable the fullscreen/maximized "
|
||||
"state. 0 - behind, 1 - takes over, 2 - unfullscreen/unmaxize [0/1/2]",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||
.data = SConfigOptionDescription::SRangeData{0, 0, 2},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:exit_window_retains_fullscreen",
|
||||
|
|
@ -1316,14 +1298,8 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:disable_hyprland_guiutils_check",
|
||||
.description = "disable the warning if hyprland-guiutils is missing",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:disable_watchdog_warning",
|
||||
.description = "whether to disable the warning about not using start-hyprland.",
|
||||
.value = "misc:disable_hyprland_qtutils_check",
|
||||
.description = "disable the warning if hyprland-qtutils is missing",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
|
@ -1343,7 +1319,7 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.value = "misc:anr_missed_pings",
|
||||
.description = "number of missed pings before showing the ANR dialog",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{5, 1, 20},
|
||||
.data = SConfigOptionDescription::SRangeData{1, 1, 10},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:screencopy_force_8b",
|
||||
|
|
@ -1351,18 +1327,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:disable_scale_notification",
|
||||
.description = "disables notification popup when a monitor fails to set a suitable scale and falls back to suggested",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "misc:size_limits_tiled",
|
||||
.description = "whether to apply minsize and maxsize rules to tiled windows",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* binds:
|
||||
|
|
@ -1554,19 +1518,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:non_shader_cm",
|
||||
.description = "Enable CM without shader. 0 - disable, 1 - whenever possible, 2 - DS and passthrough only, 3 - disable and ignore CM issues",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "disable,always,ondemand,ignore"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "render:cm_sdr_eotf",
|
||||
.description = "Default transfer function for displaying SDR apps. 0 - Treat unspecified as sRGB, 1 - Treat unspecified as Gamma 2.2, 2 - Treat "
|
||||
"unspecified and sRGB as Gamma 2.2",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{0, "srgb,gamma22,gamma22force"},
|
||||
},
|
||||
|
||||
/*
|
||||
* cursor:
|
||||
|
|
@ -1652,18 +1603,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:zoom_disable_aa",
|
||||
.description = "If enabled, when zooming, no antialiasing will be used (zoom will be pixelated)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:zoom_detached_camera",
|
||||
.description = "Detaches the camera from the mouse when zoomed in, only ever moving to keep the mouse in view",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:enable_hyprcursor",
|
||||
.description = "whether to enable hyprcursor support",
|
||||
|
|
@ -1682,12 +1621,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:hide_on_tablet",
|
||||
.description = "Hides the cursor when the last input was a tablet input until a mouse input is done.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "cursor:use_cpu_buffer",
|
||||
.description = "Makes HW cursors use a CPU buffer. Required on Nvidia to have HW cursors. Experimental",
|
||||
|
|
@ -1964,6 +1897,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"left"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:inherit_fullscreen",
|
||||
.description = "inherit fullscreen status when cycling/swapping to another window (e.g. monocle layout)",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "master:slave_count_for_center_master",
|
||||
.description = "when using orientation=center, make the master window centered only when at least this many slave windows are open. (Set 0 to always_center_master)",
|
||||
|
|
@ -2011,16 +1950,4 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* Quirks
|
||||
*/
|
||||
|
||||
SConfigOptionDescription{
|
||||
.value = "quirks:prefer_hdr",
|
||||
.description = "Prefer HDR mode. 0 - off, 1 - always, 2 - gamescope only",
|
||||
.type = CONFIG_OPTION_INT,
|
||||
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 2},
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,16 +12,16 @@
|
|||
#include <functional>
|
||||
#include <xf86drmMode.h>
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../desktop/view/Window.hpp"
|
||||
#include "../desktop/Window.hpp"
|
||||
#include "../desktop/LayerRule.hpp"
|
||||
|
||||
#include "ConfigDataValues.hpp"
|
||||
#include "../SharedDefs.hpp"
|
||||
#include "../helpers/Color.hpp"
|
||||
#include "../desktop/DesktopTypes.hpp"
|
||||
#include "../desktop/reserved/ReservedArea.hpp"
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include "../desktop/WindowRule.hpp"
|
||||
#include "../managers/XWaylandManager.hpp"
|
||||
#include "../managers/KeybindManager.hpp"
|
||||
|
||||
#include <hyprlang.hpp>
|
||||
|
||||
|
|
@ -49,6 +49,13 @@ struct SWorkspaceRule {
|
|||
std::map<std::string, std::string> layoutopts;
|
||||
};
|
||||
|
||||
struct SMonitorAdditionalReservedArea {
|
||||
int top = 0;
|
||||
int bottom = 0;
|
||||
int left = 0;
|
||||
int right = 0;
|
||||
};
|
||||
|
||||
struct SPluginKeyword {
|
||||
HANDLE handle = nullptr;
|
||||
std::string name = "";
|
||||
|
|
@ -60,6 +67,11 @@ 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*/
|
||||
|
|
@ -179,7 +191,7 @@ class CMonitorRuleParser {
|
|||
|
||||
void setDisabled();
|
||||
void setMirror(const std::string& value);
|
||||
bool setReserved(const Desktop::CReservedArea& value);
|
||||
bool setReserved(const SMonitorAdditionalReservedArea& value);
|
||||
|
||||
private:
|
||||
SMonitorRule m_rule;
|
||||
|
|
@ -190,34 +202,39 @@ class CConfigManager {
|
|||
public:
|
||||
CConfigManager();
|
||||
|
||||
void init();
|
||||
void reload();
|
||||
std::string verify();
|
||||
void init();
|
||||
void reload();
|
||||
std::string verify();
|
||||
|
||||
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
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);
|
||||
int getDeviceInt(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
float getDeviceFloat(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
Vector2D getDeviceVec(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
std::string getDeviceString(const std::string&, const std::string&, const std::string& fallback = "");
|
||||
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 = "");
|
||||
std::string getMainConfigPath();
|
||||
std::string getConfigString();
|
||||
void* const* getConfigValuePtr(const std::string&);
|
||||
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
|
||||
std::string getMainConfigPath();
|
||||
std::string getConfigString();
|
||||
|
||||
SMonitorRule getMonitorRuleFor(const PHLMONITOR);
|
||||
SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace);
|
||||
std::string getDefaultWorkspaceFor(const std::string&);
|
||||
SMonitorRule getMonitorRuleFor(const PHLMONITOR);
|
||||
SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace);
|
||||
std::string getDefaultWorkspaceFor(const std::string&);
|
||||
|
||||
PHLMONITOR getBoundMonitorForWS(const std::string&);
|
||||
std::string getBoundMonitorStringForWS(const std::string&);
|
||||
const std::vector<SWorkspaceRule>& getAllWorkspaceRules();
|
||||
PHLMONITOR getBoundMonitorForWS(const std::string&);
|
||||
std::string getBoundMonitorStringForWS(const std::string&);
|
||||
const std::vector<SWorkspaceRule>& getAllWorkspaceRules();
|
||||
|
||||
void ensurePersistentWorkspacesPresent();
|
||||
std::vector<SP<CWindowRule>> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
|
||||
std::vector<SP<CLayerRule>> getMatchingRules(PHLLS);
|
||||
void ensurePersistentWorkspacesPresent();
|
||||
|
||||
const std::vector<SConfigOptionDescription>& getAllDescriptions();
|
||||
const std::vector<SConfigOptionDescription>& getAllDescriptions();
|
||||
|
||||
std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas;
|
||||
|
||||
const std::unordered_map<std::string, SP<Hyprutils::Animation::SAnimationPropertyConfig>>& getAnimationConfig();
|
||||
|
||||
|
|
@ -242,6 +259,8 @@ class CConfigManager {
|
|||
|
||||
SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&);
|
||||
|
||||
void addExecRule(const SExecRequestedRule&);
|
||||
|
||||
void handlePluginLoads();
|
||||
std::string getErrors();
|
||||
|
||||
|
|
@ -254,24 +273,22 @@ class CConfigManager {
|
|||
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBind(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBezier(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleSource(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleEnv(const std::string&, const std::string&);
|
||||
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
|
||||
std::optional<std::string> handlePermission(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleGesture(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleWindowrule(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleLayerrule(const std::string&, const std::string&);
|
||||
|
||||
std::optional<std::string> handleMonitorv2(const std::string& output);
|
||||
Hyprlang::CParseResult handleMonitorv2();
|
||||
std::optional<std::string> addRuleFromConfigKey(const std::string& name);
|
||||
std::optional<std::string> addLayerRuleFromConfigKey(const std::string& name);
|
||||
Hyprlang::CParseResult reloadRules();
|
||||
|
||||
std::string m_configCurrentPath;
|
||||
|
||||
|
|
@ -290,18 +307,21 @@ class CConfigManager {
|
|||
|
||||
Hyprutils::Animation::CAnimationConfigTree m_animationTree;
|
||||
|
||||
SSubmap m_currentSubmap;
|
||||
std::string m_currentSubmap = ""; // For storing the current keybind submap
|
||||
|
||||
std::vector<SExecRequestedRule> m_execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
||||
|
||||
std::vector<std::string> m_declaredPlugins;
|
||||
std::vector<SPluginKeyword> m_pluginKeywords;
|
||||
std::vector<SPluginVariable> m_pluginVariables;
|
||||
|
||||
std::vector<SP<Desktop::Rule::IRule>> m_keywordRules;
|
||||
|
||||
bool m_isFirstLaunch = true; // For exec-once
|
||||
|
||||
std::vector<SMonitorRule> m_monitorRules;
|
||||
std::vector<SWorkspaceRule> m_workspaceRules;
|
||||
std::vector<SP<CWindowRule>> m_windowRules;
|
||||
std::vector<SP<CLayerRule>> m_layerRules;
|
||||
std::vector<std::string> m_blurLSNamespaces;
|
||||
|
||||
bool m_firstExecDispatched = false;
|
||||
bool m_manualCrashInitiated = false;
|
||||
|
|
@ -315,11 +335,11 @@ class CConfigManager {
|
|||
uint32_t m_configValueNumber = 0;
|
||||
|
||||
// internal methods
|
||||
void updateBlurredLS(const std::string&, const bool);
|
||||
void setDefaultAnimationVars();
|
||||
std::optional<std::string> resetHLConfig();
|
||||
std::optional<std::string> generateConfig(std::string configPath, bool safeMode = false);
|
||||
std::optional<std::string> generateConfig(std::string configPath);
|
||||
std::optional<std::string> verifyConfigExists();
|
||||
void reloadRuleConfigs();
|
||||
|
||||
void postConfigReload(const Hyprlang::CParseResult& result);
|
||||
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
#include "ConfigWatcher.hpp"
|
||||
#if defined(__linux__)
|
||||
#include <linux/limits.h>
|
||||
#endif
|
||||
#include <sys/inotify.h>
|
||||
#include "../debug/log/Logger.hpp"
|
||||
#include "../debug/Log.hpp"
|
||||
#include <ranges>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -13,14 +11,14 @@ using namespace Hyprutils::OS;
|
|||
|
||||
CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) {
|
||||
if (!m_inotifyFd.isValid()) {
|
||||
Log::logger->log(Log::ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
|
||||
Debug::log(ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: make CFileDescriptor take F_GETFL, F_SETFL
|
||||
const int FLAGS = fcntl(m_inotifyFd.get(), F_GETFL, 0);
|
||||
if (fcntl(m_inotifyFd.get(), F_SETFL, FLAGS | O_NONBLOCK) < 0) {
|
||||
Log::logger->log(Log::ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
|
||||
Debug::log(ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
|
||||
m_inotifyFd.reset();
|
||||
return;
|
||||
}
|
||||
|
|
@ -78,19 +76,19 @@ void CConfigWatcher::onInotifyEvent() {
|
|||
const auto* ev = rc<const inotify_event*>(buffer.data() + offset);
|
||||
|
||||
if (offset + sizeof(inotify_event) > sc<size_t>(bytesRead)) {
|
||||
Log::logger->log(Log::ERR, "CConfigWatcher: malformed inotify event, truncated header");
|
||||
Debug::log(ERR, "CConfigWatcher: malformed inotify event, truncated header");
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset + sizeof(inotify_event) + ev->len > sc<size_t>(bytesRead)) {
|
||||
Log::logger->log(Log::ERR, "CConfigWatcher: malformed inotify event, truncated name field");
|
||||
Debug::log(ERR, "CConfigWatcher: malformed inotify event, truncated name field");
|
||||
break;
|
||||
}
|
||||
|
||||
const auto WD = std::ranges::find_if(m_watches, [wd = ev->wd](const auto& e) { return e.wd == wd; });
|
||||
|
||||
if (WD == m_watches.end())
|
||||
Log::logger->log(Log::ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev->wd);
|
||||
Debug::log(ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev->wd);
|
||||
else
|
||||
m_watchCallback(SConfigWatchEvent{
|
||||
.file = WD->file,
|
||||
|
|
|
|||
|
|
@ -6,43 +6,36 @@
|
|||
#include <cerrno>
|
||||
#include <sys/stat.h>
|
||||
#include <filesystem>
|
||||
#include "../../helpers/MiscFunctions.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
|
||||
#include "../../plugins/PluginSystem.hpp"
|
||||
#include "SignalSafe.hpp"
|
||||
#include "../plugins/PluginSystem.hpp"
|
||||
#include "../signal-safe.hpp"
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
static char const* const MESSAGES[] = {
|
||||
"Sorry, didn't mean to...",
|
||||
"This was an accident, I swear!",
|
||||
"Calm down, it was a misinput! MISINPUT!",
|
||||
"Oops",
|
||||
"Vaxry is going to be upset.",
|
||||
"Who tried dividing by zero?!",
|
||||
"Maybe you should try dusting your PC in the meantime?",
|
||||
"I tried so hard, and got so far...",
|
||||
"I don't feel so good...",
|
||||
"*thud*",
|
||||
"Well this is awkward.",
|
||||
"\"stable\"",
|
||||
"I hope you didn't have any unsaved progress.",
|
||||
"All these computers...",
|
||||
"The math isn't mathing...",
|
||||
"We've got an imposter in the code!",
|
||||
"Well, at least the crash reporter didn't crash!",
|
||||
"Everything's just fi-",
|
||||
"Have you tried asking Hyprland politely not to crash?",
|
||||
};
|
||||
static char const* const MESSAGES[] = {"Sorry, didn't mean to...",
|
||||
"This was an accident, I swear!",
|
||||
"Calm down, it was a misinput! MISINPUT!",
|
||||
"Oops",
|
||||
"Vaxry is going to be upset.",
|
||||
"Who tried dividing by zero?!",
|
||||
"Maybe you should try dusting your PC in the meantime?",
|
||||
"I tried so hard, and got so far...",
|
||||
"I don't feel so good...",
|
||||
"*thud*",
|
||||
"Well this is awkward.",
|
||||
"\"stable\"",
|
||||
"I hope you didn't have any unsaved progress.",
|
||||
"All these computers..."};
|
||||
|
||||
// <random> is not async-signal-safe, fake it with time(NULL) instead
|
||||
static char const* getRandomMessage() {
|
||||
char const* getRandomMessage() {
|
||||
return MESSAGES[time(nullptr) % (sizeof(MESSAGES) / sizeof(MESSAGES[0]))];
|
||||
}
|
||||
|
||||
[[noreturn]] static inline void exitWithError(char const* err) {
|
||||
[[noreturn]] inline void exitWithError(char const* err) {
|
||||
write(STDERR_FILENO, err, strlen(err));
|
||||
// perror() is not signal-safe, but we use it here
|
||||
// because if the crash-handler already crashed, it can't get any worse.
|
||||
|
|
@ -50,17 +43,17 @@ static char const* getRandomMessage() {
|
|||
abort();
|
||||
}
|
||||
|
||||
void CrashReporter::createAndSaveCrash(int sig) {
|
||||
void NCrashReporter::createAndSaveCrash(int sig) {
|
||||
int reportFd = -1;
|
||||
|
||||
// We're in the signal handler, so we *only* have stack memory.
|
||||
// To save as much stack memory as possible,
|
||||
// destroy things as soon as possible.
|
||||
{
|
||||
SignalSafe::CMaxLengthCString<255> reportPath;
|
||||
CMaxLengthCString<255> reportPath;
|
||||
|
||||
const auto HOME = SignalSafe::getenv("HOME");
|
||||
const auto CACHE_HOME = SignalSafe::getenv("XDG_CACHE_HOME");
|
||||
const auto HOME = sigGetenv("HOME");
|
||||
const auto CACHE_HOME = sigGetenv("XDG_CACHE_HOME");
|
||||
|
||||
if (CACHE_HOME && CACHE_HOME[0] != '\0') {
|
||||
reportPath += CACHE_HOME;
|
||||
|
|
@ -74,30 +67,32 @@ void CrashReporter::createAndSaveCrash(int sig) {
|
|||
}
|
||||
|
||||
int ret = mkdir(reportPath.getStr(), S_IRWXU);
|
||||
if (ret < 0 && errno != EEXIST)
|
||||
//__asm__("int $3");
|
||||
if (ret < 0 && errno != EEXIST) {
|
||||
exitWithError("failed to mkdir() crash report directory\n");
|
||||
|
||||
}
|
||||
reportPath += "/hyprlandCrashReport";
|
||||
reportPath.writeNum(getpid());
|
||||
reportPath += ".txt";
|
||||
|
||||
{
|
||||
SignalSafe::CBufFileWriter<64> stderrOut(STDERR_FILENO);
|
||||
stderrOut += "Hyprland has crashed :( Consult the crash report at ";
|
||||
if (!reportPath.boundsExceeded())
|
||||
stderrOut += reportPath.getStr();
|
||||
else
|
||||
stderrOut += "[ERROR: Crash report path does not fit into memory! Check if your $CACHE_HOME/$HOME is too deeply nested. Max 255 characters.]";
|
||||
|
||||
stderrOut += " for more information.\n";
|
||||
stderrOut.flush();
|
||||
CBufFileWriter<64> stderr(2);
|
||||
stderr += "Hyprland has crashed :( Consult the crash report at ";
|
||||
if (!reportPath.boundsExceeded()) {
|
||||
stderr += reportPath.getStr();
|
||||
} else {
|
||||
stderr += "[ERROR: Crash report path does not fit into memory! Check if your $CACHE_HOME/$HOME is too deeply nested. Max 255 characters.]";
|
||||
}
|
||||
stderr += " for more information.\n";
|
||||
stderr.flush();
|
||||
}
|
||||
|
||||
reportFd = open(reportPath.getStr(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (reportFd < 0)
|
||||
if (reportFd < 0) {
|
||||
exitWithError("Failed to open crash report path for writing");
|
||||
}
|
||||
}
|
||||
SignalSafe::CBufFileWriter<512> finalCrashReport(reportFd);
|
||||
CBufFileWriter<512> finalCrashReport(reportFd);
|
||||
|
||||
finalCrashReport += "--------------------------------------------\n Hyprland Crash Report\n--------------------------------------------\n";
|
||||
finalCrashReport += getRandomMessage();
|
||||
|
|
@ -106,7 +101,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
|
|||
finalCrashReport += "Hyprland received signal ";
|
||||
finalCrashReport.writeNum(sig);
|
||||
finalCrashReport += '(';
|
||||
finalCrashReport += SignalSafe::strsignal(sig);
|
||||
finalCrashReport += sigStrsignal(sig);
|
||||
finalCrashReport += ")\nVersion: ";
|
||||
finalCrashReport += GIT_COMMIT_HASH;
|
||||
finalCrashReport += "\nTag: ";
|
||||
|
|
@ -170,10 +165,6 @@ void CrashReporter::createAndSaveCrash(int sig) {
|
|||
finalCrashReport += "\n\nos-release:\n";
|
||||
finalCrashReport.writeCmdOutput("cat /etc/os-release | sed 's/^/\t/'");
|
||||
|
||||
finalCrashReport += '\n';
|
||||
finalCrashReport += getBuiltSystemLibraryNames();
|
||||
finalCrashReport += '\n';
|
||||
|
||||
// dladdr1()/backtrace_symbols()/this entire section allocates, and hence is NOT async-signal-safe.
|
||||
// Make sure that we save the current known crash report information,
|
||||
// so that if we are caught in a deadlock during a call to malloc(),
|
||||
|
|
@ -248,5 +239,5 @@ void CrashReporter::createAndSaveCrash(int sig) {
|
|||
|
||||
finalCrashReport += "\n\nLog tail:\n";
|
||||
|
||||
finalCrashReport += Log::logger->rolling();
|
||||
finalCrashReport += std::string_view(Debug::m_rollingLog).substr(Debug::m_rollingLog.find('\n') + 1);
|
||||
}
|
||||
7
src/debug/CrashReporter.hpp
Normal file
7
src/debug/CrashReporter.hpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
|
||||
namespace NCrashReporter {
|
||||
void createAndSaveCrash(int sig);
|
||||
};
|
||||
|
|
@ -40,13 +40,10 @@ using namespace Hyprutils::OS;
|
|||
#include "../devices/ITouch.hpp"
|
||||
#include "../devices/Tablet.hpp"
|
||||
#include "../protocols/GlobalShortcuts.hpp"
|
||||
#include "debug/log/RollingLogFollow.hpp"
|
||||
#include "debug/RollingLogFollow.hpp"
|
||||
#include "config/ConfigManager.hpp"
|
||||
#include "helpers/MiscFunctions.hpp"
|
||||
#include "../desktop/view/LayerSurface.hpp"
|
||||
#include "../desktop/rule/Engine.hpp"
|
||||
#include "../desktop/history/WindowHistoryTracker.hpp"
|
||||
#include "../desktop/state/FocusState.hpp"
|
||||
#include "../desktop/LayerSurface.hpp"
|
||||
#include "../version.h"
|
||||
|
||||
#include "../Compositor.hpp"
|
||||
|
|
@ -114,14 +111,13 @@ static std::string availableModesForOutput(PHLMONITOR pMonitor, eHyprCtlOutputFo
|
|||
}
|
||||
|
||||
const std::array<const char*, CMonitor::SC_CHECKS_COUNT> SOLITARY_REASONS_JSON = {
|
||||
"\"UNKNOWN\"", "\"NOTIFICATION\"", "\"LOCK\"", "\"WORKSPACE\"", "\"WINDOWED\"", "\"DND\"", "\"SPECIAL\"", "\"ALPHA\"", "\"OFFSET\"",
|
||||
"\"CANDIDATE\"", "\"OPAQUE\"", "\"TRANSFORM\"", "\"OVERLAYS\"", "\"FLOAT\"", "\"WORKSPACES\"", "\"SURFACES\"", "\"CONFIGERROR\"",
|
||||
"\"UNKNOWN\"", "\"NOTIFICATION\"", "\"LOCK\"", "\"WORKSPACE\"", "\"WINDOWED\"", "\"DND\"", "\"SPECIAL\"", "\"ALPHA\"",
|
||||
"\"OFFSET\"", "\"CANDIDATE\"", "\"OPAQUE\"", "\"TRANSFORM\"", "\"OVERLAYS\"", "\"FLOAT\"", "\"WORKSPACES\"", "\"SURFACES\"",
|
||||
};
|
||||
|
||||
const std::array<const char*, CMonitor::SC_CHECKS_COUNT> SOLITARY_REASONS_TEXT = {
|
||||
"unknown reason", "notification", "session lock", "invalid workspace", "windowed mode", "dnd active",
|
||||
"special workspace", "alpha channel", "workspace offset", "missing candidate", "not opaque", "surface transformations",
|
||||
"other overlays", "floating windows", "other workspaces", "subsurfaces", "config error",
|
||||
"unknown reason", "notification", "session lock", "invalid workspace", "windowed mode", "dnd active", "special workspace", "alpha channel",
|
||||
"workspace offset", "missing candidate", "not opaque", "surface transformations", "other overlays", "floating windows", "other workspaces", "subsurfaces",
|
||||
};
|
||||
|
||||
std::string CHyprCtl::getSolitaryBlockedReason(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) {
|
||||
|
|
@ -132,7 +128,7 @@ std::string CHyprCtl::getSolitaryBlockedReason(Hyprutils::Memory::CSharedPointer
|
|||
std::string reasonStr = "";
|
||||
const auto TEXTS = format == eHyprCtlOutputFormat::FORMAT_JSON ? SOLITARY_REASONS_JSON : SOLITARY_REASONS_TEXT;
|
||||
|
||||
for (uint32_t i = 0; i < CMonitor::SC_CHECKS_COUNT; i++) {
|
||||
for (int i = 0; i < CMonitor::SC_CHECKS_COUNT; i++) {
|
||||
if (reasons & (1 << i)) {
|
||||
if (reasonStr != "")
|
||||
reasonStr += ",";
|
||||
|
|
@ -244,25 +240,19 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
|
|||
"disabled": {},
|
||||
"currentFormat": "{}",
|
||||
"mirrorOf": "{}",
|
||||
"availableModes": [{}],
|
||||
"colorManagementPreset": "{}",
|
||||
"sdrBrightness": {:.2f},
|
||||
"sdrSaturation": {:.2f},
|
||||
"sdrMinLuminance": {:.2f},
|
||||
"sdrMaxLuminance": {}
|
||||
"availableModes": [{}]
|
||||
}},)#",
|
||||
|
||||
m->m_id, escapeJSONStrings(m->m_name), escapeJSONStrings(m->m_shortDescription), escapeJSONStrings(m->m_output->make), escapeJSONStrings(m->m_output->model),
|
||||
escapeJSONStrings(m->m_output->serial), sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), sc<int>(m->m_output->physicalSize.x),
|
||||
sc<int>(m->m_output->physicalSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->activeWorkspaceID(),
|
||||
(!m->m_activeWorkspace ? "" : escapeJSONStrings(m->m_activeWorkspace->m_name)), m->activeSpecialWorkspaceID(),
|
||||
escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc<int>(m->m_reservedArea.left()), sc<int>(m->m_reservedArea.top()),
|
||||
sc<int>(m->m_reservedArea.right()), sc<int>(m->m_reservedArea.bottom()), m->m_scale, sc<int>(m->m_transform),
|
||||
(m == Desktop::focusState()->monitor() ? "true" : "false"), (m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"),
|
||||
rc<uint64_t>(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), (m->m_tearingState.activelyTearing ? "true" : "false"),
|
||||
getTearingBlockedReason(m, format), rc<uint64_t>(m->m_lastScanout.get()), getDSBlockedReason(m, format), (m->m_enabled ? "false" : "true"),
|
||||
formatToString(m->m_output->state->state().drmFormat), m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format),
|
||||
(NCMType::toString(m->m_cmType)), (m->m_sdrBrightness), (m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance));
|
||||
escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc<int>(m->m_reservedTopLeft.x), sc<int>(m->m_reservedTopLeft.y),
|
||||
sc<int>(m->m_reservedBottomRight.x), sc<int>(m->m_reservedBottomRight.y), m->m_scale, sc<int>(m->m_transform), (m == g_pCompositor->m_lastMonitor ? "true" : "false"),
|
||||
(m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"), rc<uint64_t>(m->m_solitaryClient.get()),
|
||||
getSolitaryBlockedReason(m, format), (m->m_tearingState.activelyTearing ? "true" : "false"), getTearingBlockedReason(m, format), rc<uint64_t>(m->m_lastScanout.get()),
|
||||
getDSBlockedReason(m, format), (m->m_enabled ? "false" : "true"), formatToString(m->m_output->state->state().drmFormat),
|
||||
m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format));
|
||||
|
||||
} else {
|
||||
result += std::format(
|
||||
|
|
@ -271,16 +261,15 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
|
|||
"dpmsStatus: {}\n\tvrr: {}\n\tsolitary: {:x}\n\tsolitaryBlockedBy: {}\n\tactivelyTearing: {}\n\ttearingBlockedBy: {}\n\tdirectScanoutTo: "
|
||||
"{:x}\n\tdirectScanoutBlockedBy: {}\n\tdisabled: "
|
||||
"{}\n\tcurrentFormat: {}\n\tmirrorOf: "
|
||||
"{}\n\tavailableModes: {}\n\tcolorManagementPreset: {}\n\tsdrBrightness: {:.2f}\n\tsdrSaturation: {:.2f}\n\tsdrMinLuminance: {:.2f}\n\tsdrMaxLuminance: {}\n\n",
|
||||
"{}\n\tavailableModes: {}\n\n",
|
||||
m->m_name, m->m_id, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->m_shortDescription,
|
||||
m->m_output->make, m->m_output->model, sc<int>(m->m_output->physicalSize.x), sc<int>(m->m_output->physicalSize.y), m->m_output->serial, m->activeWorkspaceID(),
|
||||
(!m->m_activeWorkspace ? "" : m->m_activeWorkspace->m_name), m->activeSpecialWorkspaceID(), (m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""),
|
||||
sc<int>(m->m_reservedArea.left()), sc<int>(m->m_reservedArea.top()), sc<int>(m->m_reservedArea.right()), sc<int>(m->m_reservedArea.bottom()), m->m_scale,
|
||||
sc<int>(m->m_transform), (m == Desktop::focusState()->monitor() ? "yes" : "no"), sc<int>(m->m_dpmsStatus), m->m_output->state->state().adaptiveSync,
|
||||
sc<int>(m->m_reservedTopLeft.x), sc<int>(m->m_reservedTopLeft.y), sc<int>(m->m_reservedBottomRight.x), sc<int>(m->m_reservedBottomRight.y), m->m_scale,
|
||||
sc<int>(m->m_transform), (m == g_pCompositor->m_lastMonitor ? "yes" : "no"), sc<int>(m->m_dpmsStatus), m->m_output->state->state().adaptiveSync,
|
||||
rc<uint64_t>(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), m->m_tearingState.activelyTearing, getTearingBlockedReason(m, format),
|
||||
rc<uint64_t>(m->m_lastScanout.get()), getDSBlockedReason(m, format), !m->m_enabled, formatToString(m->m_output->state->state().drmFormat),
|
||||
m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format), (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness),
|
||||
(m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance));
|
||||
m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -320,7 +309,7 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ
|
|||
}
|
||||
|
||||
static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||
const auto tags = w->m_ruleApplicator->m_tagKeeper.getTags();
|
||||
const auto tags = w->m_tags.getTags();
|
||||
|
||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
|
||||
return std::ranges::fold_left(tags, std::string(),
|
||||
|
|
@ -355,10 +344,9 @@ static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
|
||||
std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||
auto getFocusHistoryID = [](PHLWINDOW wnd) -> int {
|
||||
const auto& HISTORY = Desktop::History::windowTracker()->fullHistory();
|
||||
for (size_t i = 0; i < HISTORY.size(); ++i) {
|
||||
if (HISTORY[i].lock() == wnd)
|
||||
return HISTORY.size() - i - 1; // reverse order for backwards compat
|
||||
for (size_t i = 0; i < g_pCompositor->m_windowFocusHistory.size(); ++i) {
|
||||
if (g_pCompositor->m_windowFocusHistory[i].lock() == wnd)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
|
@ -393,8 +381,7 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
"focusHistoryID": {},
|
||||
"inhibitingIdle": {},
|
||||
"xdgTag": "{}",
|
||||
"xdgDescription": "{}",
|
||||
"contentType": "{}"
|
||||
"xdgDescription": "{}"
|
||||
}},)#",
|
||||
rc<uintptr_t>(w.get()), (w->m_isMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), sc<int>(w->m_realPosition->goal().x),
|
||||
sc<int>(w->m_realPosition->goal().y), sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
||||
|
|
@ -402,21 +389,20 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
w->monitorID(), escapeJSONStrings(w->m_class), escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle), w->getPID(),
|
||||
(sc<int>(w->m_isX11) == 1 ? "true" : "false"), (w->m_pinned ? "true" : "false"), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client),
|
||||
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w),
|
||||
(g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")),
|
||||
escapeJSONStrings(NContentType::toString(w->getContentType())));
|
||||
(g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")));
|
||||
} else {
|
||||
return std::format(
|
||||
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
|
||||
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
|
||||
"{}\n\txwayland: {}\n\tpinned: "
|
||||
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: {}\n\txdgTag: "
|
||||
"{}\n\txdgDescription: {}\n\tcontentType: {}\n\n",
|
||||
"{}\n\txdgDescription: {}\n\n",
|
||||
rc<uintptr_t>(w.get()), w->m_title, sc<int>(w->m_isMapped), sc<int>(w->isHidden()), sc<int>(w->m_realPosition->goal().x), sc<int>(w->m_realPosition->goal().y),
|
||||
sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
||||
(!w->m_workspace ? "" : w->m_workspace->m_name), sc<int>(w->m_isFloating), sc<int>(w->m_isPseudotiled), w->monitorID(), w->m_class, w->m_title, w->m_initialClass,
|
||||
w->m_initialTitle, w->getPID(), sc<int>(w->m_isX11), sc<int>(w->m_pinned), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client),
|
||||
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w), sc<int>(g_pInputManager->isWindowInhibiting(w, false)),
|
||||
w->xdgTag().value_or(""), w->xdgDescription().value_or(""), NContentType::toString(w->getContentType()));
|
||||
w->xdgTag().value_or(""), w->xdgDescription().value_or(""));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -527,11 +513,11 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF
|
|||
}
|
||||
|
||||
static std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||
if (!Desktop::focusState()->monitor())
|
||||
if (!g_pCompositor->m_lastMonitor)
|
||||
return "unsafe state";
|
||||
|
||||
std::string result = "";
|
||||
auto w = Desktop::focusState()->monitor()->m_activeWorkspace;
|
||||
auto w = g_pCompositor->m_lastMonitor->m_activeWorkspace;
|
||||
|
||||
if (!valid(w))
|
||||
return "internal error";
|
||||
|
|
@ -581,7 +567,7 @@ static std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::strin
|
|||
}
|
||||
|
||||
static std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||
const auto PWINDOW = Desktop::focusState()->window();
|
||||
const auto PWINDOW = g_pCompositor->m_lastWindow.lock();
|
||||
|
||||
if (!validMapped(PWINDOW))
|
||||
return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
|
||||
|
|
@ -959,10 +945,11 @@ static std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string re
|
|||
|
||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
||||
result += "[\n\"log\":\"";
|
||||
result += escapeJSONStrings(Log::logger->rolling());
|
||||
result += escapeJSONStrings(Debug::m_rollingLog);
|
||||
result += "\"]";
|
||||
} else
|
||||
result = Log::logger->rolling();
|
||||
} else {
|
||||
result = Debug::m_rollingLog;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -1012,7 +999,7 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request
|
|||
ret += "d";
|
||||
|
||||
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdescription: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb->modmask,
|
||||
kb->submap.name, kb->key, kb->keycode, kb->catchAll, kb->description, kb->handler, kb->arg);
|
||||
kb->submap, kb->key, kb->keycode, kb->catchAll, kb->description, kb->handler, kb->arg);
|
||||
}
|
||||
} else {
|
||||
// json
|
||||
|
|
@ -1030,7 +1017,6 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request
|
|||
"has_description": {},
|
||||
"modmask": {},
|
||||
"submap": "{}",
|
||||
"submap_universal": "{}",
|
||||
"key": "{}",
|
||||
"keycode": {},
|
||||
"catch_all": {},
|
||||
|
|
@ -1039,9 +1025,8 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request
|
|||
"arg": "{}"
|
||||
}},)#",
|
||||
kb->locked ? "true" : "false", kb->mouse ? "true" : "false", kb->release ? "true" : "false", kb->repeat ? "true" : "false", kb->longPress ? "true" : "false",
|
||||
kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap.name), kb->submapUniversal,
|
||||
escapeJSONStrings(kb->key), kb->keycode, kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler),
|
||||
escapeJSONStrings(kb->arg));
|
||||
kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap), escapeJSONStrings(kb->key), kb->keycode,
|
||||
kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler), escapeJSONStrings(kb->arg));
|
||||
}
|
||||
trimTrailingComma(ret);
|
||||
ret += "]";
|
||||
|
|
@ -1058,17 +1043,12 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
|||
if (format == eHyprCtlOutputFormat::FORMAT_NORMAL) {
|
||||
std::string result = std::format("Hyprland {} built from branch {} at commit {} {} ({}).\n"
|
||||
"Date: {}\n"
|
||||
"Tag: {}, commits: {}\n",
|
||||
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS);
|
||||
"Tag: {}, commits: {}\n"
|
||||
"built against:\n aquamarine {}\n hyprlang {}\n hyprutils {}\n hyprcursor {}\n hyprgraphics {}\n\n\n",
|
||||
HYPRLAND_VERSION, GIT_BRANCH, GIT_COMMIT_HASH, GIT_DIRTY, commitMsg, GIT_COMMIT_DATE, GIT_TAG, GIT_COMMITS, AQUAMARINE_VERSION,
|
||||
HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION);
|
||||
|
||||
result += "\n";
|
||||
result += getBuiltSystemLibraryNames();
|
||||
result += "\n";
|
||||
result += "Version ABI string: ";
|
||||
result += __hyprland_api_get_hash();
|
||||
result += "\n";
|
||||
|
||||
#if (!ISDEBUG && !defined(NO_XWAYLAND) && !defined(BUILT_WITH_NIX))
|
||||
#if (!ISDEBUG && !defined(NO_XWAYLAND))
|
||||
result += "no flags were set\n";
|
||||
#else
|
||||
result += "flags set:\n";
|
||||
|
|
@ -1078,9 +1058,6 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
|||
#ifdef NO_XWAYLAND
|
||||
result += "no xwayland\n";
|
||||
#endif
|
||||
#ifdef BUILT_WITH_NIX
|
||||
result += "nix\n";
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
} else {
|
||||
|
|
@ -1099,17 +1076,9 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
|||
"buildHyprutils": "{}",
|
||||
"buildHyprcursor": "{}",
|
||||
"buildHyprgraphics": "{}",
|
||||
"systemAquamarine": "{}",
|
||||
"systemHyprlang": "{}",
|
||||
"systemHyprutils": "{}",
|
||||
"systemHyprcursor": "{}",
|
||||
"systemHyprgraphics": "{}",
|
||||
"abiHash": "{}",
|
||||
"flags": [)#",
|
||||
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
|
||||
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION, getSystemLibraryVersion("aquamarine"),
|
||||
getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics"),
|
||||
__hyprland_api_get_hash());
|
||||
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION);
|
||||
|
||||
#if ISDEBUG
|
||||
result += "\"debug\",";
|
||||
|
|
@ -1117,9 +1086,6 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
|||
#ifdef NO_XWAYLAND
|
||||
result += "\"no xwayland\",";
|
||||
#endif
|
||||
#ifdef BUILT_WITH_NIX
|
||||
result += "\"nix\",";
|
||||
#endif
|
||||
|
||||
trimTrailingComma(result);
|
||||
|
||||
|
|
@ -1155,9 +1121,6 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request)
|
|||
result += "Node name: " + std::string{unameInfo.nodename} + "\n";
|
||||
result += "Release: " + std::string{unameInfo.release} + "\n";
|
||||
result += "Version: " + std::string{unameInfo.version} + "\n";
|
||||
result += "\n";
|
||||
result += getBuiltSystemLibraryNames();
|
||||
result += "\n";
|
||||
|
||||
result += "\n\n";
|
||||
|
||||
|
|
@ -1216,24 +1179,19 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request)
|
|||
} else
|
||||
result += "\tunknown: not runtime\n";
|
||||
|
||||
if (g_pHyprOpenGL) {
|
||||
result += std::format("\nExplicit sync: {}", g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext ? "supported" : "missing");
|
||||
result += std::format("\nGL ver: {}", g_pHyprOpenGL->m_eglContextVersion == CHyprOpenGLImpl::EGL_CONTEXT_GLES_3_2 ? "3.2" : "3.0");
|
||||
}
|
||||
result += std::format("\nExplicit sync: {}", g_pHyprOpenGL->m_exts.EGL_ANDROID_native_fence_sync_ext ? "supported" : "missing");
|
||||
result += std::format("\nGL ver: {}", g_pHyprOpenGL->m_eglContextVersion == CHyprOpenGLImpl::EGL_CONTEXT_GLES_3_2 ? "3.2" : "3.0");
|
||||
result += std::format("\nBackend: {}", g_pCompositor->m_aqBackend->hasSession() ? "drm" : "sessionless");
|
||||
|
||||
if (g_pCompositor) {
|
||||
result += std::format("\nBackend: {}", g_pCompositor->m_aqBackend->hasSession() ? "drm" : "sessionless");
|
||||
result += "\n\nMonitor info:";
|
||||
|
||||
result += "\n\nMonitor info:";
|
||||
|
||||
for (const auto& m : g_pCompositor->m_monitors) {
|
||||
result += std::format("\n\tPanel {}: {}x{}, {} {} {} {} -> backend {}\n\t\texplicit {}\n\t\tedid:\n\t\t\thdr {}\n\t\t\tchroma {}\n\t\t\tbt2020 {}\n\t\tvrr capable "
|
||||
"{}\n\t\tnon-desktop {}\n\t\t",
|
||||
m->m_name, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_output->name, m->m_output->make, m->m_output->model, m->m_output->serial,
|
||||
backend(m->m_output->getBackend()->type()), check(m->m_output->supportsExplicit), check(m->m_output->parsedEDID.hdrMetadata.has_value()),
|
||||
check(m->m_output->parsedEDID.chromaticityCoords.has_value()), check(m->m_output->parsedEDID.supportsBT2020), check(m->m_output->vrrCapable),
|
||||
check(m->m_output->nonDesktop));
|
||||
}
|
||||
for (const auto& m : g_pCompositor->m_monitors) {
|
||||
result += std::format("\n\tPanel {}: {}x{}, {} {} {} {} -> backend {}\n\t\texplicit {}\n\t\tedid:\n\t\t\thdr {}\n\t\t\tchroma {}\n\t\t\tbt2020 {}\n\t\tvrr capable "
|
||||
"{}\n\t\tnon-desktop {}\n\t\t",
|
||||
m->m_name, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_output->name, m->m_output->make, m->m_output->model, m->m_output->serial,
|
||||
backend(m->m_output->getBackend()->type()), check(m->m_output->supportsExplicit), check(m->m_output->parsedEDID.hdrMetadata.has_value()),
|
||||
check(m->m_output->parsedEDID.chromaticityCoords.has_value()), check(m->m_output->parsedEDID.supportsBT2020), check(m->m_output->vrrCapable),
|
||||
check(m->m_output->nonDesktop));
|
||||
}
|
||||
|
||||
if (g_pHyprCtl && g_pHyprCtl->m_currentRequestParams.sysInfoConfig) {
|
||||
|
|
@ -1261,7 +1219,7 @@ static std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in)
|
|||
|
||||
SDispatchResult res = DISPATCHER->second(DISPATCHARG);
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error);
|
||||
Debug::log(LOG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error);
|
||||
|
||||
return res.success ? "ok" : res.error;
|
||||
}
|
||||
|
|
@ -1288,12 +1246,8 @@ 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
|
||||
|
|
@ -1326,7 +1280,8 @@ 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") {
|
||||
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
|
||||
COMMAND.starts_with("windowrule")) {
|
||||
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
*(m->m_cursorZoom) = *PZOOMFACTOR;
|
||||
|
|
@ -1335,13 +1290,10 @@ 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();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);
|
||||
Debug::log(LOG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);
|
||||
|
||||
if (retval.empty())
|
||||
return "ok";
|
||||
|
|
@ -1543,6 +1495,11 @@ 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, ' ');
|
||||
|
||||
|
|
@ -1560,9 +1517,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_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
|
||||
auto sizeValue = PWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
|
||||
if (max)
|
||||
sizeValue = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D(INFINITY, INFINITY));
|
||||
sizeValue = PWINDOW->m_windowData.maxSize.valueOr(Vector2D(INFINITY, INFINITY));
|
||||
|
||||
if (FORMNORM)
|
||||
return std::format("{} {}", sizeValue.x, sizeValue.y);
|
||||
|
|
@ -1573,7 +1530,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
|||
}
|
||||
};
|
||||
|
||||
auto alphaToString = [&](Desktop::Types::COverridableVar<Desktop::Types::SAlphaValue>& alpha, bool getAlpha) -> std::string {
|
||||
auto alphaToString = [&](CWindowOverridableVar<SAlphaValue>& alpha, bool getAlpha) -> std::string {
|
||||
if (FORMNORM) {
|
||||
if (getAlpha)
|
||||
return std::format("{}", alpha.valueOrDefault().alpha);
|
||||
|
|
@ -1607,7 +1564,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_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR).toString();
|
||||
std::string borderColorString = PWINDOW->m_windowData.activeBorderColor.valueOr(*ACTIVECOLOR).toString();
|
||||
if (FORMNORM)
|
||||
return borderColorString;
|
||||
else
|
||||
|
|
@ -1620,7 +1577,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_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR).toString();
|
||||
std::string borderColorString = PWINDOW->m_windowData.inactiveBorderColor.valueOr(*INACTIVECOLOR).toString();
|
||||
if (FORMNORM)
|
||||
return borderColorString;
|
||||
else
|
||||
|
|
@ -1635,92 +1592,38 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
|||
return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
|
||||
};
|
||||
|
||||
if (PROP == "animation") {
|
||||
auto& animationStyle = PWINDOW->m_ruleApplicator->animationStyle();
|
||||
if (PROP == "animationstyle") {
|
||||
auto& animationStyle = PWINDOW->m_windowData.animationStyle;
|
||||
if (FORMNORM)
|
||||
return animationStyle.valueOr("(unset)");
|
||||
else
|
||||
return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
|
||||
} else if (PROP == "max_size")
|
||||
} else if (PROP == "maxsize")
|
||||
return sizeToString(true);
|
||||
else if (PROP == "min_size")
|
||||
else if (PROP == "minsize")
|
||||
return sizeToString(false);
|
||||
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")
|
||||
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")
|
||||
return borderColorToString(true);
|
||||
else if (PROP == "inactive_border_color")
|
||||
else if (PROP == "inactivebordercolor")
|
||||
return borderColorToString(false);
|
||||
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());
|
||||
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));
|
||||
|
||||
return "prop not found";
|
||||
}
|
||||
|
|
@ -2042,7 +1945,7 @@ static std::string getDescriptions(eHyprCtlOutputFormat format, std::string requ
|
|||
}
|
||||
|
||||
static std::string submapRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||
std::string submap = g_pKeybindManager->getCurrentSubmap().name;
|
||||
std::string submap = g_pKeybindManager->getCurrentSubmap();
|
||||
if (submap.empty())
|
||||
submap = "default";
|
||||
|
||||
|
|
@ -2085,6 +1988,7 @@ 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});
|
||||
|
|
@ -2200,7 +2104,8 @@ std::string CHyprCtl::getReply(std::string request) {
|
|||
if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible())
|
||||
continue;
|
||||
|
||||
Desktop::Rule::ruleEngine()->updateAllRules();
|
||||
w->updateDynamicRules();
|
||||
g_pCompositor->updateWindowAnimatedDecorationValues(w);
|
||||
}
|
||||
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
|
|
@ -2224,23 +2129,23 @@ static bool successWrite(int fd, const std::string& data, bool needLog = true) {
|
|||
return true;
|
||||
|
||||
if (needLog)
|
||||
Log::logger->log(Log::ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
|
||||
Debug::log(ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void runWritingDebugLogThread(const int conn) {
|
||||
using namespace std::chrono_literals;
|
||||
Log::logger->log(Log::DEBUG, "In followlog thread, got connection, start writing: {}", conn);
|
||||
Debug::log(LOG, "In followlog thread, got connection, start writing: {}", conn);
|
||||
//will be finished, when reading side close connection
|
||||
std::thread([conn]() {
|
||||
while (Log::SRollingLogFollow::get().isRunning()) {
|
||||
if (Log::SRollingLogFollow::get().isEmpty(conn)) {
|
||||
while (Debug::SRollingLogFollow::get().isRunning()) {
|
||||
if (Debug::SRollingLogFollow::get().isEmpty(conn)) {
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto line = Log::SRollingLogFollow::get().getLog(conn);
|
||||
auto line = Debug::SRollingLogFollow::get().getLog(conn);
|
||||
if (!successWrite(conn, line))
|
||||
// We cannot write, when connection is closed. So thread will successfully exit by itself
|
||||
break;
|
||||
|
|
@ -2248,7 +2153,7 @@ static void runWritingDebugLogThread(const int conn) {
|
|||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
close(conn);
|
||||
Log::SRollingLogFollow::get().stopFor(conn);
|
||||
Debug::SRollingLogFollow::get().stopFor(conn);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
|
@ -2274,10 +2179,10 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
|||
CRED_T creds;
|
||||
uint32_t len = sizeof(creds);
|
||||
if (getsockopt(ACCEPTEDCONNECTION, CRED_LVL, CRED_OPT, &creds, &len) == -1)
|
||||
Log::logger->log(Log::ERR, "Hyprctl: failed to get peer creds");
|
||||
Debug::log(ERR, "Hyprctl: failed to get peer creds");
|
||||
else {
|
||||
g_pHyprCtl->m_currentRequestParams.pid = creds.CRED_PID;
|
||||
Log::logger->log(Log::DEBUG, "Hyprctl: new connection from pid {}", creds.CRED_PID);
|
||||
Debug::log(LOG, "Hyprctl: new connection from pid {}", creds.CRED_PID);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -2312,7 +2217,7 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
|||
try {
|
||||
reply = g_pHyprCtl->getReply(request);
|
||||
} catch (std::exception& e) {
|
||||
Log::logger->log(Log::ERR, "Error in request: {}", e.what());
|
||||
Debug::log(ERR, "Error in request: {}", e.what());
|
||||
reply = "Err: " + std::string(e.what());
|
||||
}
|
||||
|
||||
|
|
@ -2332,10 +2237,10 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
|||
successWrite(ACCEPTEDCONNECTION, reply);
|
||||
|
||||
if (isFollowUpRollingLogRequest(request)) {
|
||||
Log::logger->log(Log::DEBUG, "Followup rollinglog request received. Starting thread to write to socket.");
|
||||
Log::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION);
|
||||
Debug::log(LOG, "Followup rollinglog request received. Starting thread to write to socket.");
|
||||
Debug::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION);
|
||||
runWritingDebugLogThread(ACCEPTEDCONNECTION);
|
||||
Log::logger->log(Log::DEBUG, Log::SRollingLogFollow::get().debugInfo());
|
||||
Debug::log(LOG, Debug::SRollingLogFollow::get().debugInfo());
|
||||
} else
|
||||
close(ACCEPTEDCONNECTION);
|
||||
|
||||
|
|
@ -2352,7 +2257,7 @@ void CHyprCtl::startHyprCtlSocket() {
|
|||
m_socketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};
|
||||
|
||||
if (!m_socketFD.isValid()) {
|
||||
Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
|
||||
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2363,14 +2268,14 @@ void CHyprCtl::startHyprCtlSocket() {
|
|||
snprintf(SERVERADDRESS.sun_path, sizeof(SERVERADDRESS.sun_path), "%s", m_socketPath.c_str());
|
||||
|
||||
if (bind(m_socketFD.get(), rc<sockaddr*>(&SERVERADDRESS), SUN_LEN(&SERVERADDRESS)) < 0) {
|
||||
Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
|
||||
Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 10 max queued.
|
||||
listen(m_socketFD.get(), 10);
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Hypr socket started at {}", m_socketPath);
|
||||
Debug::log(LOG, "Hypr socket started at {}", m_socketPath);
|
||||
|
||||
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_socketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include <fstream>
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "../helpers/defer/Promise.hpp"
|
||||
#include "../desktop/view/Window.hpp"
|
||||
#include "../desktop/Window.hpp"
|
||||
#include <functional>
|
||||
#include <sys/types.h>
|
||||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
|
|
@ -25,10 +25,9 @@ class CHyprCtl {
|
|||
Hyprutils::OS::CFileDescriptor m_socketFD;
|
||||
|
||||
struct {
|
||||
bool all = false;
|
||||
bool sysInfoConfig = false;
|
||||
bool isDynamicKeyword = false;
|
||||
pid_t pid = 0;
|
||||
bool all = false;
|
||||
bool sysInfoConfig = false;
|
||||
pid_t pid = 0;
|
||||
SP<CPromise<std::string>> pendingPromise;
|
||||
} m_currentRequestParams;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include "../render/pass/TexPassElement.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../desktop/state/FocusState.hpp"
|
||||
|
||||
CHyprDebugOverlay::CHyprDebugOverlay() {
|
||||
m_texture = makeShared<CTexture>();
|
||||
|
|
@ -58,7 +57,7 @@ void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
|||
m_monitor = pMonitor;
|
||||
|
||||
// anim data too
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : Desktop::focusState()->monitor();
|
||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : g_pCompositor->m_lastMonitor.lock();
|
||||
if (PMONITORFORTICKS == pMonitor) {
|
||||
if (m_lastAnimationTicks.size() > sc<long unsigned int>(PMONITORFORTICKS->m_refreshRate))
|
||||
m_lastAnimationTicks.pop_front();
|
||||
|
|
|
|||
74
src/debug/Log.cpp
Normal file
74
src/debug/Log.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#include "Log.hpp"
|
||||
#include "../defines.hpp"
|
||||
#include "RollingLogFollow.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <print>
|
||||
#include <fcntl.h>
|
||||
|
||||
void Debug::init(const std::string& IS) {
|
||||
m_logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log");
|
||||
m_logOfs.open(m_logFile, std::ios::out | std::ios::app);
|
||||
auto handle = m_logOfs.native_handle();
|
||||
fcntl(handle, F_SETFD, FD_CLOEXEC);
|
||||
}
|
||||
|
||||
void Debug::close() {
|
||||
m_logOfs.close();
|
||||
}
|
||||
|
||||
void Debug::log(eLogLevel level, std::string str) {
|
||||
if (level == TRACE && !m_trace)
|
||||
return;
|
||||
|
||||
if (m_shuttingDown)
|
||||
return;
|
||||
|
||||
std::string coloredStr = str;
|
||||
//NOLINTBEGIN
|
||||
switch (level) {
|
||||
case LOG:
|
||||
str = "[LOG] " + str;
|
||||
coloredStr = str;
|
||||
break;
|
||||
case WARN:
|
||||
str = "[WARN] " + str;
|
||||
coloredStr = "\033[1;33m" + str + "\033[0m"; // yellow
|
||||
break;
|
||||
case ERR:
|
||||
str = "[ERR] " + str;
|
||||
coloredStr = "\033[1;31m" + str + "\033[0m"; // red
|
||||
break;
|
||||
case CRIT:
|
||||
str = "[CRITICAL] " + str;
|
||||
coloredStr = "\033[1;35m" + str + "\033[0m"; // magenta
|
||||
break;
|
||||
case INFO:
|
||||
str = "[INFO] " + str;
|
||||
coloredStr = "\033[1;32m" + str + "\033[0m"; // green
|
||||
break;
|
||||
case TRACE:
|
||||
str = "[TRACE] " + str;
|
||||
coloredStr = "\033[1;34m" + str + "\033[0m"; // blue
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
//NOLINTEND
|
||||
|
||||
m_rollingLog += str + "\n";
|
||||
if (m_rollingLog.size() > ROLLING_LOG_SIZE)
|
||||
m_rollingLog = m_rollingLog.substr(m_rollingLog.size() - ROLLING_LOG_SIZE);
|
||||
|
||||
if (SRollingLogFollow::get().isRunning())
|
||||
SRollingLogFollow::get().addLog(str);
|
||||
|
||||
if (!m_disableLogs || !**m_disableLogs) {
|
||||
// log to a file
|
||||
m_logOfs << str << "\n";
|
||||
m_logOfs.flush();
|
||||
}
|
||||
|
||||
// log it to the stdout too.
|
||||
if (!m_disableStdout)
|
||||
std::println("{}", ((m_coloredLogs && !**m_coloredLogs) ? str : coloredStr));
|
||||
}
|
||||
77
src/debug/Log.hpp
Normal file
77
src/debug/Log.hpp
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
#define LOGMESSAGESIZE 1024
|
||||
#define ROLLING_LOG_SIZE 4096
|
||||
|
||||
enum eLogLevel : int8_t {
|
||||
NONE = -1,
|
||||
LOG = 0,
|
||||
WARN,
|
||||
ERR,
|
||||
CRIT,
|
||||
INFO,
|
||||
TRACE
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
namespace Debug {
|
||||
inline std::string m_logFile;
|
||||
inline std::ofstream m_logOfs;
|
||||
inline int64_t* const* m_disableLogs = nullptr;
|
||||
inline int64_t* const* m_disableTime = nullptr;
|
||||
inline bool m_disableStdout = false;
|
||||
inline bool m_trace = false;
|
||||
inline bool m_shuttingDown = false;
|
||||
inline int64_t* const* m_coloredLogs = nullptr;
|
||||
|
||||
inline std::string m_rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log
|
||||
inline std::mutex m_logMutex;
|
||||
|
||||
void init(const std::string& IS);
|
||||
void close();
|
||||
|
||||
//
|
||||
void log(eLogLevel level, std::string str);
|
||||
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
void log(eLogLevel level, std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::lock_guard<std::mutex> guard(m_logMutex);
|
||||
|
||||
if (level == TRACE && !m_trace)
|
||||
return;
|
||||
|
||||
if (m_shuttingDown)
|
||||
return;
|
||||
|
||||
std::string logMsg = "";
|
||||
|
||||
// print date and time to the ofs
|
||||
if (m_disableTime && !**m_disableTime) {
|
||||
#ifndef _LIBCPP_VERSION
|
||||
static auto current_zone = std::chrono::current_zone();
|
||||
const auto zt = std::chrono::zoned_time{current_zone, std::chrono::system_clock::now()};
|
||||
const auto hms = std::chrono::hh_mm_ss{zt.get_local_time() - std::chrono::floor<std::chrono::days>(zt.get_local_time())};
|
||||
#else
|
||||
// TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready
|
||||
const auto hms = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now())};
|
||||
#endif
|
||||
logMsg += std::format("[{}] ", hms);
|
||||
}
|
||||
|
||||
// no need for try {} catch {} because std::format_string<Args...> ensures that vformat never throw std::format_error
|
||||
// because
|
||||
// 1. any faulty format specifier that sucks will cause a compilation error.
|
||||
// 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.)
|
||||
// 3. this is actually what std::format in stdlib does
|
||||
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
log(level, logMsg);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <format>
|
||||
#include <vector>
|
||||
|
||||
namespace Log {
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
namespace Debug {
|
||||
struct SRollingLogFollow {
|
||||
std::unordered_map<int, std::string> m_socketToRollingLogFollowQueue;
|
||||
std::shared_mutex m_mutex;
|
||||
|
|
@ -32,14 +30,12 @@ namespace Log {
|
|||
return ret;
|
||||
};
|
||||
|
||||
void addLog(const std::string_view& log) {
|
||||
void addLog(const std::string& log) {
|
||||
std::unique_lock<std::shared_mutex> w(m_mutex);
|
||||
m_running = true;
|
||||
std::vector<int> to_erase;
|
||||
for (const auto& p : m_socketToRollingLogFollowQueue) {
|
||||
m_socketToRollingLogFollowQueue[p.first] += log;
|
||||
m_socketToRollingLogFollowQueue[p.first] += "\n";
|
||||
}
|
||||
for (const auto& p : m_socketToRollingLogFollowQueue)
|
||||
m_socketToRollingLogFollowQueue[p.first] += log + "\n";
|
||||
}
|
||||
|
||||
bool isRunning() {
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#ifdef USE_TRACY_GPU
|
||||
|
||||
#include "log/Logger.hpp"
|
||||
#include "Log.hpp"
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace CrashReporter {
|
||||
void createAndSaveCrash(int sig);
|
||||
};
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "defines.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace SignalSafe {
|
||||
template <uint16_t N>
|
||||
class CMaxLengthCString {
|
||||
public:
|
||||
CMaxLengthCString() {
|
||||
m_str[0] = '\0';
|
||||
}
|
||||
|
||||
void operator+=(char const* rhs) {
|
||||
write(rhs, strlen(rhs));
|
||||
}
|
||||
|
||||
void write(char const* data, size_t len) {
|
||||
if (m_boundsExceeded || m_strPos + len >= N) {
|
||||
m_boundsExceeded = true;
|
||||
return;
|
||||
}
|
||||
memcpy(m_str + m_strPos, data, len);
|
||||
m_strPos += len;
|
||||
m_str[m_strPos] = '\0';
|
||||
}
|
||||
|
||||
void write(char c) {
|
||||
if (m_boundsExceeded || m_strPos + 1 >= N) {
|
||||
m_boundsExceeded = true;
|
||||
return;
|
||||
}
|
||||
m_str[m_strPos] = c;
|
||||
m_strPos++;
|
||||
}
|
||||
|
||||
void writeNum(size_t num) {
|
||||
size_t d = 1;
|
||||
|
||||
while (num / 10 >= d) {
|
||||
d *= 10;
|
||||
}
|
||||
|
||||
while (num > 0) {
|
||||
char c = '0' + (num / d);
|
||||
write(c);
|
||||
num %= d;
|
||||
d /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
char const* getStr() {
|
||||
return m_str;
|
||||
}
|
||||
|
||||
bool boundsExceeded() {
|
||||
return m_boundsExceeded;
|
||||
}
|
||||
|
||||
private:
|
||||
char m_str[N];
|
||||
size_t m_strPos = 0;
|
||||
bool m_boundsExceeded = false;
|
||||
};
|
||||
|
||||
template <uint16_t BUFSIZE>
|
||||
class CBufFileWriter {
|
||||
public:
|
||||
CBufFileWriter(int fd_) : m_fd(fd_) {
|
||||
;
|
||||
}
|
||||
|
||||
~CBufFileWriter() {
|
||||
flush();
|
||||
}
|
||||
|
||||
void write(char const* data, size_t len) {
|
||||
while (len > 0) {
|
||||
size_t to_add = std::min(len, sc<size_t>(BUFSIZE) - m_writeBufPos);
|
||||
memcpy(m_writeBuf + m_writeBufPos, data, to_add);
|
||||
data += to_add;
|
||||
len -= to_add;
|
||||
m_writeBufPos += to_add;
|
||||
if (m_writeBufPos == BUFSIZE)
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
void write(char c) {
|
||||
if (m_writeBufPos == BUFSIZE)
|
||||
flush();
|
||||
m_writeBuf[m_writeBufPos] = c;
|
||||
m_writeBufPos++;
|
||||
}
|
||||
|
||||
void operator+=(char const* str) {
|
||||
write(str, strlen(str));
|
||||
}
|
||||
|
||||
void operator+=(std::string_view str) {
|
||||
write(str.data(), str.size());
|
||||
}
|
||||
|
||||
void operator+=(char c) {
|
||||
write(c);
|
||||
}
|
||||
|
||||
void writeNum(size_t num) {
|
||||
size_t d = 1;
|
||||
|
||||
while (num / 10 >= d) {
|
||||
d *= 10;
|
||||
}
|
||||
|
||||
while (num > 0) {
|
||||
char c = '0' + (num / d);
|
||||
write(c);
|
||||
num %= d;
|
||||
d /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
void writeCmdOutput(const char* cmd) {
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) < 0) {
|
||||
*this += "<pipe(pipefd) failed with";
|
||||
writeNum(errno);
|
||||
*this += ">\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// terminate child instead of waiting
|
||||
{
|
||||
struct sigaction act;
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = SA_NOCLDWAIT;
|
||||
#ifdef SA_RESTORER
|
||||
act.sa_restorer = NULL;
|
||||
#endif
|
||||
sigaction(SIGCHLD, &act, nullptr);
|
||||
}
|
||||
|
||||
const pid_t pid = fork();
|
||||
|
||||
if (pid < 0) {
|
||||
*this += "<fork() failed with ";
|
||||
writeNum(errno);
|
||||
*this += ">\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
close(pipefd[0]);
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
char const* const argv[] = {"/bin/sh", "-c", cmd, nullptr};
|
||||
execv("/bin/sh", cc<char* const*>(argv));
|
||||
|
||||
CBufFileWriter<64> failmsg(pipefd[1]);
|
||||
failmsg += "<execv(";
|
||||
failmsg += cmd;
|
||||
failmsg += ") resulted in errno ";
|
||||
failmsg.write(errno);
|
||||
failmsg += ">\n";
|
||||
close(pipefd[1]);
|
||||
abort();
|
||||
} else {
|
||||
close(pipefd[1]);
|
||||
int64_t len = 0;
|
||||
char readbuf[256];
|
||||
while ((len = read(pipefd[0], readbuf, 256)) > 0) {
|
||||
write(readbuf, len);
|
||||
}
|
||||
if (len < 0) {
|
||||
*this += "<interrupted, read() resulted in errno ";
|
||||
writeNum(errno);
|
||||
*this += ">\n";
|
||||
}
|
||||
close(pipefd[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void flush() {
|
||||
size_t i = 0;
|
||||
while (i < m_writeBufPos) {
|
||||
auto written = ::write(m_fd, m_writeBuf + i, m_writeBufPos - i);
|
||||
if (written <= 0) {
|
||||
return;
|
||||
}
|
||||
i += written;
|
||||
}
|
||||
m_writeBufPos = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
char m_writeBuf[BUFSIZE] = {0};
|
||||
size_t m_writeBufPos = 0;
|
||||
int m_fd = 0;
|
||||
};
|
||||
|
||||
char const* getenv(const char* name);
|
||||
char const* strsignal(int sig);
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
#include "Logger.hpp"
|
||||
#include "RollingLogFollow.hpp"
|
||||
|
||||
#include "../../defines.hpp"
|
||||
|
||||
#include "../../managers/HookSystemManager.hpp"
|
||||
#include "../../config/ConfigValue.hpp"
|
||||
|
||||
using namespace Log;
|
||||
|
||||
CLogger::CLogger() {
|
||||
const auto IS_TRACE = Env::isTrace();
|
||||
m_logger.setLogLevel(IS_TRACE ? Hyprutils::CLI::LOG_TRACE : Hyprutils::CLI::LOG_DEBUG);
|
||||
}
|
||||
|
||||
void CLogger::log(Hyprutils::CLI::eLogLevel level, const std::string_view& str) {
|
||||
|
||||
static bool TRACE = Env::isTrace();
|
||||
|
||||
if (!m_logsEnabled)
|
||||
return;
|
||||
|
||||
if (level == Hyprutils::CLI::LOG_TRACE && !TRACE)
|
||||
return;
|
||||
|
||||
if (SRollingLogFollow::get().isRunning())
|
||||
SRollingLogFollow::get().addLog(str);
|
||||
|
||||
m_logger.log(level, str);
|
||||
}
|
||||
|
||||
void CLogger::initIS(const std::string_view& IS) {
|
||||
// NOLINTNEXTLINE
|
||||
m_logger.setOutputFile(std::string{IS} + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log"));
|
||||
m_logger.setEnableRolling(true);
|
||||
m_logger.setEnableColor(false);
|
||||
m_logger.setEnableStdout(true);
|
||||
m_logger.setTime(false);
|
||||
}
|
||||
|
||||
void CLogger::initCallbacks() {
|
||||
static auto P = g_pHookSystem->hookDynamic("configReloaded", [this](void* hk, SCallbackInfo& info, std::any param) { recheckCfg(); });
|
||||
recheckCfg();
|
||||
}
|
||||
|
||||
void CLogger::recheckCfg() {
|
||||
static auto PDISABLELOGS = CConfigValue<Hyprlang::INT>("debug:disable_logs");
|
||||
static auto PDISABLETIME = CConfigValue<Hyprlang::INT>("debug:disable_time");
|
||||
static auto PENABLESTDOUT = CConfigValue<Hyprlang::INT>("debug:enable_stdout_logs");
|
||||
static auto PENABLECOLOR = CConfigValue<Hyprlang::INT>("debug:colored_stdout_logs");
|
||||
|
||||
m_logger.setEnableStdout(!*PDISABLELOGS && *PENABLESTDOUT);
|
||||
m_logsEnabled = !*PDISABLELOGS;
|
||||
m_logger.setTime(!*PDISABLETIME);
|
||||
m_logger.setEnableColor(*PENABLECOLOR);
|
||||
}
|
||||
|
||||
const std::string& CLogger::rolling() {
|
||||
return m_logger.rollingLog();
|
||||
}
|
||||
|
||||
Hyprutils::CLI::CLogger& CLogger::hu() {
|
||||
return m_logger;
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprutils/cli/Logger.hpp>
|
||||
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
#include "../../helpers/env/Env.hpp"
|
||||
|
||||
namespace Log {
|
||||
class CLogger {
|
||||
public:
|
||||
CLogger();
|
||||
~CLogger() = default;
|
||||
|
||||
void initIS(const std::string_view& IS);
|
||||
void initCallbacks();
|
||||
|
||||
void log(Hyprutils::CLI::eLogLevel level, const std::string_view& str);
|
||||
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
void log(Hyprutils::CLI::eLogLevel level, std::format_string<Args...> fmt, Args&&... args) {
|
||||
static bool TRACE = Env::isTrace();
|
||||
|
||||
if (!m_logsEnabled)
|
||||
return;
|
||||
|
||||
if (level == Hyprutils::CLI::LOG_TRACE && !TRACE)
|
||||
return;
|
||||
|
||||
std::string logMsg = "";
|
||||
|
||||
// no need for try {} catch {} because std::format_string<Args...> ensures that vformat never throw std::format_error
|
||||
// because
|
||||
// 1. any faulty format specifier that sucks will cause a compilation error.
|
||||
// 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.)
|
||||
// 3. this is actually what std::format in stdlib does
|
||||
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
log(level, logMsg);
|
||||
}
|
||||
|
||||
const std::string& rolling();
|
||||
Hyprutils::CLI::CLogger& hu();
|
||||
|
||||
private:
|
||||
void recheckCfg();
|
||||
|
||||
Hyprutils::CLI::CLogger m_logger;
|
||||
bool m_logsEnabled = true;
|
||||
};
|
||||
|
||||
inline UP<CLogger> logger = makeUnique<CLogger>();
|
||||
|
||||
//
|
||||
inline constexpr const Hyprutils::CLI::eLogLevel DEBUG = Hyprutils::CLI::LOG_DEBUG;
|
||||
inline constexpr const Hyprutils::CLI::eLogLevel WARN = Hyprutils::CLI::LOG_WARN;
|
||||
inline constexpr const Hyprutils::CLI::eLogLevel ERR = Hyprutils::CLI::LOG_ERR;
|
||||
inline constexpr const Hyprutils::CLI::eLogLevel CRIT = Hyprutils::CLI::LOG_CRIT;
|
||||
inline constexpr const Hyprutils::CLI::eLogLevel INFO = Hyprutils::CLI::LOG_DEBUG;
|
||||
inline constexpr const Hyprutils::CLI::eLogLevel TRACE = Hyprutils::CLI::LOG_TRACE;
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
#include "debug/log/Logger.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "helpers/Color.hpp"
|
||||
#include "macros.hpp"
|
||||
#include "desktop/DesktopTypes.hpp"
|
||||
|
|
|
|||
|
|
@ -1,30 +1,26 @@
|
|||
#pragma once
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
|
||||
class CWorkspace;
|
||||
class CWindow;
|
||||
class CLayerSurface;
|
||||
class CMonitor;
|
||||
|
||||
namespace Desktop::View {
|
||||
class CWindow;
|
||||
class CLayerSurface;
|
||||
}
|
||||
|
||||
/* Shared pointer to a workspace */
|
||||
using PHLWORKSPACE = SP<CWorkspace>;
|
||||
/* Weak pointer to a workspace */
|
||||
using PHLWORKSPACEREF = WP<CWorkspace>;
|
||||
|
||||
/* Shared pointer to a window */
|
||||
using PHLWINDOW = SP<Desktop::View::CWindow>;
|
||||
using PHLWINDOW = SP<CWindow>;
|
||||
/* Weak pointer to a window */
|
||||
using PHLWINDOWREF = WP<Desktop::View::CWindow>;
|
||||
using PHLWINDOWREF = WP<CWindow>;
|
||||
|
||||
/* Shared pointer to a layer surface */
|
||||
using PHLLS = SP<Desktop::View::CLayerSurface>;
|
||||
using PHLLS = SP<CLayerSurface>;
|
||||
/* Weak pointer to a layer surface */
|
||||
using PHLLSREF = WP<Desktop::View::CLayerSurface>;
|
||||
using PHLLSREF = WP<CLayerSurface>;
|
||||
|
||||
/* Shared pointer to a monitor */
|
||||
using PHLMONITOR = SP<CMonitor>;
|
||||
/* Weak pointer to a monitor */
|
||||
using PHLMONITORREF = WP<CMonitor>;
|
||||
using PHLMONITORREF = WP<CMonitor>;
|
||||
|
|
|
|||
40
src/desktop/LayerRule.cpp
Normal file
40
src/desktop/LayerRule.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#include <re2/re2.h>
|
||||
#include "LayerRule.hpp"
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include "../debug/Log.hpp"
|
||||
|
||||
static const auto RULES = std::unordered_set<std::string>{"noanim", "blur", "blurpopups", "dimaround"};
|
||||
static const auto RULES_PREFIX = std::unordered_set<std::string>{"ignorealpha", "ignorezero", "xray", "animation", "order", "abovelock"};
|
||||
|
||||
CLayerRule::CLayerRule(const std::string& rule_, const std::string& ns_) : m_targetNamespace(ns_), m_rule(rule_) {
|
||||
const bool VALID = RULES.contains(m_rule) || std::ranges::any_of(RULES_PREFIX, [&rule_](const auto& prefix) { return rule_.starts_with(prefix); });
|
||||
|
||||
if (!VALID)
|
||||
return;
|
||||
|
||||
if (m_rule == "noanim")
|
||||
m_ruleType = RULE_NOANIM;
|
||||
else if (m_rule == "blur")
|
||||
m_ruleType = RULE_BLUR;
|
||||
else if (m_rule == "blurpopups")
|
||||
m_ruleType = RULE_BLURPOPUPS;
|
||||
else if (m_rule == "dimaround")
|
||||
m_ruleType = RULE_DIMAROUND;
|
||||
else if (m_rule.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;
|
||||
}
|
||||
}
|
||||
32
src/desktop/LayerRule.hpp
Normal file
32
src/desktop/LayerRule.hpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include "Rule.hpp"
|
||||
|
||||
class CLayerRule {
|
||||
public:
|
||||
CLayerRule(const std::string& rule, const std::string& targetNS);
|
||||
|
||||
enum eRuleType : uint8_t {
|
||||
RULE_INVALID = 0,
|
||||
RULE_NOANIM,
|
||||
RULE_BLUR,
|
||||
RULE_BLURPOPUPS,
|
||||
RULE_DIMAROUND,
|
||||
RULE_ABOVELOCK,
|
||||
RULE_IGNOREALPHA,
|
||||
RULE_IGNOREZERO,
|
||||
RULE_XRAY,
|
||||
RULE_ANIMATION,
|
||||
RULE_ORDER,
|
||||
RULE_ZUMBA,
|
||||
};
|
||||
|
||||
eRuleType m_ruleType = RULE_INVALID;
|
||||
|
||||
const std::string m_targetNamespace;
|
||||
const std::string m_rule;
|
||||
|
||||
CRuleRegexContainer m_targetNamespaceRegex;
|
||||
};
|
||||
|
|
@ -1,30 +1,27 @@
|
|||
#include "LayerSurface.hpp"
|
||||
#include "../state/FocusState.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include "../../protocols/LayerShell.hpp"
|
||||
#include "../../protocols/core/Compositor.hpp"
|
||||
#include "../../managers/SeatManager.hpp"
|
||||
#include "../../managers/animation/AnimationManager.hpp"
|
||||
#include "../../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
#include "../../helpers/Monitor.hpp"
|
||||
#include "../../managers/input/InputManager.hpp"
|
||||
#include "../../managers/HookSystemManager.hpp"
|
||||
#include "../../managers/EventManager.hpp"
|
||||
|
||||
using namespace Desktop;
|
||||
using namespace Desktop::View;
|
||||
#include "../Compositor.hpp"
|
||||
#include "../events/Events.hpp"
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../managers/SeatManager.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../managers/EventManager.hpp"
|
||||
|
||||
PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
|
||||
PHLLS pLS = SP<CLayerSurface>(new CLayerSurface(resource));
|
||||
|
||||
auto pMonitor = resource->m_monitor.empty() ? Desktop::focusState()->monitor() : g_pCompositor->getMonitorFromName(resource->m_monitor);
|
||||
auto pMonitor = resource->m_monitor.empty() ? g_pCompositor->m_lastMonitor.lock() : g_pCompositor->getMonitorFromName(resource->m_monitor);
|
||||
|
||||
pLS->m_wlSurface->assign(resource->m_surface.lock(), pLS);
|
||||
pLS->m_surface->assign(resource->m_surface.lock(), pLS);
|
||||
|
||||
if (!pMonitor) {
|
||||
Log::logger->log(Log::ERR, "New LS has no monitor??");
|
||||
Debug::log(ERR, "New LS has no monitor??");
|
||||
return pLS;
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +37,7 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
|
|||
pLS->m_monitor = pMonitor;
|
||||
pMonitor->m_layerSurfaceLayers[resource->m_current.layer].emplace_back(pLS);
|
||||
|
||||
pLS->m_ruleApplicator = makeUnique<Desktop::Rule::CLayerRuleApplicator>(pLS);
|
||||
pLS->m_forceBlur = g_pConfigManager->shouldBlurLS(pLS->m_namespace);
|
||||
|
||||
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);
|
||||
|
|
@ -50,38 +47,34 @@ PHLLS CLayerSurface::create(SP<CLayerShellResource> resource) {
|
|||
|
||||
pLS->m_alpha->setValueAndWarp(0.f);
|
||||
|
||||
Log::logger->log(Log::DEBUG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", rc<uintptr_t>(resource.get()), resource->m_layerNamespace,
|
||||
sc<int>(pLS->m_layer), pMonitor->m_name);
|
||||
Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", rc<uintptr_t>(resource.get()), resource->m_layerNamespace, sc<int>(pLS->m_layer),
|
||||
pMonitor->m_name);
|
||||
|
||||
return pLS;
|
||||
}
|
||||
|
||||
PHLLS CLayerSurface::fromView(SP<IView> v) {
|
||||
if (!v || v->type() != VIEW_TYPE_LAYER_SURFACE)
|
||||
return nullptr;
|
||||
return dynamicPointerCast<CLayerSurface>(v);
|
||||
}
|
||||
|
||||
void CLayerSurface::registerCallbacks() {
|
||||
m_alpha->setUpdateCallback([this](auto) {
|
||||
if (m_ruleApplicator->dimAround().valueOrDefault() && m_monitor)
|
||||
if (m_dimAround && m_monitor)
|
||||
g_pHyprRenderer->damageMonitor(m_monitor.lock());
|
||||
});
|
||||
}
|
||||
|
||||
CLayerSurface::CLayerSurface(SP<CLayerShellResource> resource_) : IView(CWLSurface::create()), m_layerSurface(resource_) {
|
||||
CLayerSurface::CLayerSurface(SP<CLayerShellResource> resource_) : m_layerSurface(resource_) {
|
||||
m_listeners.commit = m_layerSurface->m_events.commit.listen([this] { onCommit(); });
|
||||
m_listeners.map = m_layerSurface->m_events.map.listen([this] { onMap(); });
|
||||
m_listeners.unmap = m_layerSurface->m_events.unmap.listen([this] { onUnmap(); });
|
||||
m_listeners.destroy = m_layerSurface->m_events.destroy.listen([this] { onDestroy(); });
|
||||
|
||||
m_surface = CWLSurface::create();
|
||||
}
|
||||
|
||||
CLayerSurface::~CLayerSurface() {
|
||||
if (!g_pHyprOpenGL)
|
||||
return;
|
||||
|
||||
if (m_wlSurface)
|
||||
m_wlSurface->unassign();
|
||||
if (m_surface)
|
||||
m_surface->unassign();
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
std::erase_if(g_pHyprOpenGL->m_layerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == m_self.lock(); });
|
||||
|
||||
|
|
@ -92,43 +85,20 @@ CLayerSurface::~CLayerSurface() {
|
|||
}
|
||||
}
|
||||
|
||||
eViewType CLayerSurface::type() const {
|
||||
return VIEW_TYPE_LAYER_SURFACE;
|
||||
}
|
||||
|
||||
bool CLayerSurface::visible() const {
|
||||
return (m_mapped && m_layerSurface && m_layerSurface->m_mapped && m_wlSurface && m_wlSurface->resource()) || (m_fadingOut && m_alpha->value() > 0.F);
|
||||
}
|
||||
|
||||
std::optional<CBox> CLayerSurface::logicalBox() const {
|
||||
return surfaceLogicalBox();
|
||||
}
|
||||
|
||||
std::optional<CBox> CLayerSurface::surfaceLogicalBox() const {
|
||||
if (!visible())
|
||||
return std::nullopt;
|
||||
|
||||
return CBox{m_realPosition->value(), m_realSize->value()};
|
||||
}
|
||||
|
||||
bool CLayerSurface::desktopComponent() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CLayerSurface::onDestroy() {
|
||||
Log::logger->log(Log::DEBUG, "LayerSurface {:x} destroyed", rc<uintptr_t>(m_layerSurface.get()));
|
||||
Debug::log(LOG, "LayerSurface {:x} destroyed", rc<uintptr_t>(m_layerSurface.get()));
|
||||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
Log::logger->log(Log::WARN, "Layersurface destroyed on an invalid monitor (removed?)");
|
||||
Debug::log(WARN, "Layersurface destroyed on an invalid monitor (removed?)");
|
||||
|
||||
if (!m_fadingOut) {
|
||||
if (m_mapped) {
|
||||
Log::logger->log(Log::DEBUG, "Forcing an unmap of a LS that did a straight destroy!");
|
||||
Debug::log(LOG, "Forcing an unmap of a LS that did a straight destroy!");
|
||||
onUnmap();
|
||||
} else {
|
||||
Log::logger->log(Log::DEBUG, "Removing LayerSurface that wasn't mapped.");
|
||||
Debug::log(LOG, "Removing LayerSurface that wasn't mapped.");
|
||||
if (m_alpha)
|
||||
g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_OUT);
|
||||
m_fadingOut = true;
|
||||
|
|
@ -152,8 +122,8 @@ void CLayerSurface::onDestroy() {
|
|||
|
||||
m_readyToDelete = true;
|
||||
m_layerSurface.reset();
|
||||
if (m_wlSurface)
|
||||
m_wlSurface->unassign();
|
||||
if (m_surface)
|
||||
m_surface->unassign();
|
||||
|
||||
m_listeners.unmap.reset();
|
||||
m_listeners.destroy.reset();
|
||||
|
|
@ -162,13 +132,10 @@ void CLayerSurface::onDestroy() {
|
|||
}
|
||||
|
||||
void CLayerSurface::onMap() {
|
||||
Log::logger->log(Log::DEBUG, "LayerSurface {:x} mapped", rc<uintptr_t>(m_layerSurface.get()));
|
||||
Debug::log(LOG, "LayerSurface {:x} mapped", rc<uintptr_t>(m_layerSurface.get()));
|
||||
|
||||
m_mapped = true;
|
||||
m_interactivity = m_layerSurface->m_current.interactivity;
|
||||
m_aboveFullscreen = true;
|
||||
|
||||
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ALL);
|
||||
m_mapped = true;
|
||||
m_interactivity = m_layerSurface->m_current.interactivity;
|
||||
|
||||
m_layerSurface->m_surface->map();
|
||||
|
||||
|
|
@ -182,11 +149,13 @@ void CLayerSurface::onMap() {
|
|||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
applyRules();
|
||||
|
||||
PMONITOR->m_scheduledRecalc = true;
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->m_id);
|
||||
|
||||
m_wlSurface->resource()->enter(PMONITOR->m_self.lock());
|
||||
m_surface->resource()->enter(PMONITOR->m_self.lock());
|
||||
|
||||
const bool ISEXCLUSIVE = m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE;
|
||||
|
||||
|
|
@ -200,14 +169,14 @@ void CLayerSurface::onMap() {
|
|||
|
||||
if (GRABSFOCUS) {
|
||||
// TODO: use the new superb really very cool grab
|
||||
if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(m_wlSurface->resource()))
|
||||
if (g_pSeatManager->m_seatGrab && !g_pSeatManager->m_seatGrab->accepts(m_surface->resource()))
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
Desktop::focusState()->rawSurfaceFocus(m_wlSurface->resource());
|
||||
g_pCompositor->focusSurface(m_surface->resource());
|
||||
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
|
||||
g_pSeatManager->setPointerFocus(m_wlSurface->resource(), LOCAL);
|
||||
g_pSeatManager->setPointerFocus(m_surface->resource(), LOCAL);
|
||||
g_pInputManager->m_emptyFocusCursorSet = false;
|
||||
}
|
||||
|
||||
|
|
@ -224,12 +193,12 @@ void CLayerSurface::onMap() {
|
|||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "openlayer", .data = m_namespace});
|
||||
EMIT_HOOK_EVENT("openLayer", m_self.lock());
|
||||
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform);
|
||||
g_pCompositor->setPreferredScaleForSurface(m_surface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
|
||||
}
|
||||
|
||||
void CLayerSurface::onUnmap() {
|
||||
Log::logger->log(Log::DEBUG, "LayerSurface {:x} unmapped", rc<uintptr_t>(m_layerSurface.get()));
|
||||
Debug::log(LOG, "LayerSurface {:x} unmapped", rc<uintptr_t>(m_layerSurface.get()));
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "closelayer", .data = m_layerSurface->m_layerNamespace});
|
||||
EMIT_HOOK_EVENT("closeLayer", m_self.lock());
|
||||
|
|
@ -237,7 +206,7 @@ void CLayerSurface::onUnmap() {
|
|||
std::erase_if(g_pInputManager->m_exclusiveLSes, [this](const auto& other) { return !other || other == m_self; });
|
||||
|
||||
if (!m_monitor || g_pCompositor->m_unsafeState) {
|
||||
Log::logger->log(Log::WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
|
||||
Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring.");
|
||||
|
||||
g_pCompositor->addToFadingOutSafe(m_self.lock());
|
||||
|
||||
|
|
@ -268,19 +237,18 @@ void CLayerSurface::onUnmap() {
|
|||
|
||||
const auto PMONITOR = m_monitor.lock();
|
||||
|
||||
const bool WASLASTFOCUS = g_pSeatManager->m_state.keyboardFocus == m_wlSurface->resource() || g_pSeatManager->m_state.pointerFocus == m_wlSurface->resource();
|
||||
const bool WASLASTFOCUS = g_pSeatManager->m_state.keyboardFocus == m_surface->resource() || g_pSeatManager->m_state.pointerFocus == m_surface->resource();
|
||||
|
||||
if (!PMONITOR)
|
||||
return;
|
||||
|
||||
// refocus if needed
|
||||
// vvvvvvvvvvvvv if there is a last focus and the last focus is not keyboard focusable, fallback to window
|
||||
if (WASLASTFOCUS ||
|
||||
(Desktop::focusState()->surface() && Desktop::focusState()->surface()->m_hlSurface && !Desktop::focusState()->surface()->m_hlSurface->keyboardFocusable())) {
|
||||
if (WASLASTFOCUS || (g_pCompositor->m_lastFocus && g_pCompositor->m_lastFocus->m_hlSurface && !g_pCompositor->m_lastFocus->m_hlSurface->keyboardFocusable())) {
|
||||
if (!g_pInputManager->refocusLastWindow(PMONITOR))
|
||||
g_pInputManager->refocus();
|
||||
} else if (Desktop::focusState()->surface() && Desktop::focusState()->surface() != m_wlSurface->resource())
|
||||
g_pSeatManager->setKeyboardFocus(Desktop::focusState()->surface());
|
||||
} else if (g_pCompositor->m_lastFocus && g_pCompositor->m_lastFocus != m_surface->resource())
|
||||
g_pSeatManager->setKeyboardFocus(g_pCompositor->m_lastFocus.lock());
|
||||
|
||||
CBox geomFixed = {m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y, m_geometry.width, m_geometry.height};
|
||||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
|
@ -321,21 +289,19 @@ void CLayerSurface::onCommit() {
|
|||
g_pHyprRenderer->damageBox(geomFixed);
|
||||
|
||||
if (m_layerSurface->m_current.committed != 0) {
|
||||
if (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_LAYER && m_layerSurface->m_current.layer != m_layer) {
|
||||
if (m_layerSurface->m_current.committed & CLayerShellResource::eCommittedState::STATE_LAYER) {
|
||||
|
||||
for (auto it = PMONITOR->m_layerSurfaceLayers[m_layer].begin(); it != PMONITOR->m_layerSurfaceLayers[m_layer].end(); it++) {
|
||||
if (*it == m_self) {
|
||||
if (m_layerSurface->m_current.layer == m_layer)
|
||||
break;
|
||||
PMONITOR->m_layerSurfaceLayers[m_layerSurface->m_current.layer].emplace_back(*it);
|
||||
PMONITOR->m_layerSurfaceLayers[m_layer].erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_layer = m_layerSurface->m_current.layer;
|
||||
m_aboveFullscreen = m_layerSurface->m_current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY;
|
||||
|
||||
// if in fullscreen, only overlay can be above.
|
||||
*m_alpha = PMONITOR->inFullscreenMode() ? (m_layer >= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ? 1.F : 0.F) : 1.F;
|
||||
m_layer = m_layerSurface->m_current.layer;
|
||||
|
||||
if (m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
|
||||
g_pHyprOpenGL->markBlurDirtyForMonitor(PMONITOR); // so that blur is recalc'd
|
||||
|
|
@ -377,8 +343,8 @@ void CLayerSurface::onCommit() {
|
|||
nullptr);
|
||||
if (!WASLASTFOCUS && m_popupHead) {
|
||||
m_popupHead->breadthfirst(
|
||||
[&WASLASTFOCUS](WP<Desktop::View::CPopup> popup, void* data) {
|
||||
WASLASTFOCUS = WASLASTFOCUS || (popup->wlSurface() && g_pSeatManager->m_state.keyboardFocus == popup->wlSurface()->resource());
|
||||
[&WASLASTFOCUS](WP<CPopup> popup, void* data) {
|
||||
WASLASTFOCUS = WASLASTFOCUS || (popup->m_wlSurface && g_pSeatManager->m_state.keyboardFocus == popup->m_wlSurface->resource());
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
|
|
@ -394,7 +360,7 @@ void CLayerSurface::onCommit() {
|
|||
if (WASLASTFOCUS && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
|
||||
// moveMouseUnified won't focus non interactive layers but it won't unfocus them either,
|
||||
// so unfocus the surface here.
|
||||
Desktop::focusState()->rawSurfaceFocus(nullptr);
|
||||
g_pCompositor->focusSurface(nullptr);
|
||||
g_pInputManager->refocusLastWindow(m_monitor.lock());
|
||||
} else if (WASLASTFOCUS && WASEXCLUSIVE && m_layerSurface->m_current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
|
|
@ -402,20 +368,92 @@ void CLayerSurface::onCommit() {
|
|||
// if now exclusive and not previously
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
g_pInputManager->releaseAllMouseButtons();
|
||||
Desktop::focusState()->rawSurfaceFocus(m_wlSurface->resource());
|
||||
g_pCompositor->focusSurface(m_surface->resource());
|
||||
|
||||
const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(m_geometry.x + PMONITOR->m_position.x, m_geometry.y + PMONITOR->m_position.y);
|
||||
g_pSeatManager->setPointerFocus(m_wlSurface->resource(), LOCAL);
|
||||
g_pSeatManager->setPointerFocus(m_surface->resource(), LOCAL);
|
||||
g_pInputManager->m_emptyFocusCursorSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_interactivity = m_layerSurface->m_current.interactivity;
|
||||
|
||||
g_pHyprRenderer->damageSurface(m_wlSurface->resource(), m_position.x, m_position.y);
|
||||
g_pHyprRenderer->damageSurface(m_surface->resource(), m_position.x, m_position.y);
|
||||
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_wlSurface->resource(), PMONITOR->m_transform);
|
||||
g_pCompositor->setPreferredScaleForSurface(m_surface->resource(), PMONITOR->m_scale);
|
||||
g_pCompositor->setPreferredTransformForSurface(m_surface->resource(), PMONITOR->m_transform);
|
||||
}
|
||||
|
||||
void CLayerSurface::applyRules() {
|
||||
m_noAnimations = false;
|
||||
m_forceBlur = false;
|
||||
m_ignoreAlpha = false;
|
||||
m_ignoreAlphaValue = 0.f;
|
||||
m_dimAround = false;
|
||||
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_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() {
|
||||
|
|
@ -430,7 +468,7 @@ int CLayerSurface::popupsCount() {
|
|||
return 0;
|
||||
|
||||
int no = -1; // we have one dummy
|
||||
m_popupHead->breadthfirst([](WP<Desktop::View::CPopup> p, void* data) { *sc<int*>(data) += 1; }, &no);
|
||||
m_popupHead->breadthfirst([](WP<CPopup> p, void* data) { *sc<int*>(data) += 1; }, &no);
|
||||
return no;
|
||||
}
|
||||
|
||||
|
|
@ -442,10 +480,10 @@ pid_t CLayerSurface::getPID() {
|
|||
pid_t PID = -1;
|
||||
|
||||
if (!m_layerSurface || !m_layerSurface->m_surface || !m_layerSurface->m_surface->getResource() || !m_layerSurface->m_surface->getResource()->resource() ||
|
||||
!m_layerSurface->m_surface->getResource()->client())
|
||||
!m_layerSurface->m_surface->getResource()->resource()->client)
|
||||
return -1;
|
||||
|
||||
wl_client_get_credentials(m_layerSurface->m_surface->getResource()->client(), &PID, nullptr, nullptr);
|
||||
wl_client_get_credentials(m_layerSurface->m_surface->getResource()->resource()->client, &PID, nullptr, nullptr);
|
||||
|
||||
return PID;
|
||||
}
|
||||
106
src/desktop/LayerSurface.hpp
Normal file
106
src/desktop/LayerSurface.hpp
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "../defines.hpp"
|
||||
#include "WLSurface.hpp"
|
||||
#include "../helpers/AnimatedVariable.hpp"
|
||||
|
||||
class CLayerShellResource;
|
||||
|
||||
class CLayerSurface {
|
||||
public:
|
||||
static PHLLS create(SP<CLayerShellResource>);
|
||||
|
||||
private:
|
||||
CLayerSurface(SP<CLayerShellResource>);
|
||||
|
||||
public:
|
||||
~CLayerSurface();
|
||||
|
||||
void applyRules();
|
||||
bool isFadedOut();
|
||||
int popupsCount();
|
||||
|
||||
PHLANIMVAR<Vector2D> m_realPosition;
|
||||
PHLANIMVAR<Vector2D> m_realSize;
|
||||
PHLANIMVAR<float> m_alpha;
|
||||
|
||||
WP<CLayerShellResource> m_layerSurface;
|
||||
|
||||
// the header providing the enum type cannot be imported here
|
||||
int m_interactivity = 0;
|
||||
|
||||
SP<CWLSurface> m_surface;
|
||||
|
||||
bool m_mapped = false;
|
||||
uint32_t m_layer = 0;
|
||||
|
||||
PHLMONITORREF m_monitor;
|
||||
|
||||
bool m_fadingOut = false;
|
||||
bool m_readyToDelete = false;
|
||||
bool m_noProcess = false;
|
||||
bool m_noAnimations = 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;
|
||||
int64_t m_order = 0;
|
||||
bool m_aboveLockscreen = false;
|
||||
bool m_aboveLockscreenInteractable = false;
|
||||
|
||||
std::optional<std::string> m_animationStyle;
|
||||
|
||||
PHLLSREF m_self;
|
||||
|
||||
CBox m_geometry = {0, 0, 0, 0};
|
||||
Vector2D m_position;
|
||||
std::string m_namespace = "";
|
||||
UP<CPopup> m_popupHead;
|
||||
|
||||
pid_t getPID();
|
||||
|
||||
void onDestroy();
|
||||
void onMap();
|
||||
void onUnmap();
|
||||
void onCommit();
|
||||
MONITORID monitorID();
|
||||
|
||||
private:
|
||||
struct {
|
||||
CHyprSignalListener destroy;
|
||||
CHyprSignalListener map;
|
||||
CHyprSignalListener unmap;
|
||||
CHyprSignalListener commit;
|
||||
} m_listeners;
|
||||
|
||||
void registerCallbacks();
|
||||
|
||||
// For the list lookup
|
||||
bool operator==(const CLayerSurface& rhs) const {
|
||||
return m_layerSurface == rhs.m_layerSurface && m_monitor == rhs.m_monitor;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool valid(PHLLS l) {
|
||||
return l;
|
||||
}
|
||||
|
||||
inline bool valid(PHLLSREF l) {
|
||||
return l;
|
||||
}
|
||||
|
||||
inline bool validMapped(PHLLS l) {
|
||||
if (!valid(l))
|
||||
return false;
|
||||
return l->m_mapped;
|
||||
}
|
||||
|
||||
inline bool validMapped(PHLLSREF l) {
|
||||
if (!valid(l))
|
||||
return false;
|
||||
return l->m_mapped;
|
||||
}
|
||||
|
|
@ -1,45 +1,43 @@
|
|||
#include "Popup.hpp"
|
||||
#include "../../config/ConfigValue.hpp"
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include "../../protocols/LayerShell.hpp"
|
||||
#include "../../protocols/XDGShell.hpp"
|
||||
#include "../../protocols/core/Compositor.hpp"
|
||||
#include "../../managers/SeatManager.hpp"
|
||||
#include "../../managers/animation/AnimationManager.hpp"
|
||||
#include "LayerSurface.hpp"
|
||||
#include "../../managers/input/InputManager.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
#include "../../render/OpenGL.hpp"
|
||||
#include "../config/ConfigValue.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../protocols/XDGShell.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../managers/SeatManager.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../desktop/LayerSurface.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../render/OpenGL.hpp"
|
||||
#include <ranges>
|
||||
|
||||
using namespace Desktop;
|
||||
using namespace Desktop::View;
|
||||
|
||||
SP<CPopup> CPopup::create(PHLWINDOW pOwner) {
|
||||
auto popup = SP<CPopup>(new CPopup());
|
||||
UP<CPopup> CPopup::create(PHLWINDOW pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_windowOwner = pOwner;
|
||||
popup->m_self = popup;
|
||||
popup->initAllSignals();
|
||||
return popup;
|
||||
}
|
||||
|
||||
SP<CPopup> CPopup::create(PHLLS pOwner) {
|
||||
auto popup = SP<CPopup>(new CPopup());
|
||||
UP<CPopup> CPopup::create(PHLLS pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_layerOwner = pOwner;
|
||||
popup->m_self = popup;
|
||||
popup->initAllSignals();
|
||||
return popup;
|
||||
}
|
||||
|
||||
SP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
|
||||
auto popup = SP<CPopup>(new CPopup());
|
||||
UP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
|
||||
auto popup = UP<CPopup>(new CPopup());
|
||||
popup->m_resource = resource;
|
||||
popup->m_windowOwner = pOwner->m_windowOwner;
|
||||
popup->m_layerOwner = pOwner->m_layerOwner;
|
||||
popup->m_parent = pOwner;
|
||||
popup->m_self = popup;
|
||||
popup->wlSurface()->assign(resource->m_surface->m_surface.lock(), popup);
|
||||
popup->m_wlSurface = CWLSurface::create();
|
||||
popup->m_wlSurface->assign(resource->m_surface->m_surface.lock(), popup.get());
|
||||
|
||||
popup->m_lastSize = resource->m_surface->m_current.geometry.size();
|
||||
popup->reposition();
|
||||
|
|
@ -48,56 +46,11 @@ SP<CPopup> CPopup::create(SP<CXDGPopupResource> resource, WP<CPopup> pOwner) {
|
|||
return popup;
|
||||
}
|
||||
|
||||
SP<CPopup> CPopup::fromView(SP<IView> v) {
|
||||
if (!v || v->type() != VIEW_TYPE_POPUP)
|
||||
return nullptr;
|
||||
return dynamicPointerCast<CPopup>(v);
|
||||
}
|
||||
|
||||
CPopup::CPopup() : IView(CWLSurface::create()) {
|
||||
;
|
||||
}
|
||||
|
||||
CPopup::~CPopup() {
|
||||
if (m_wlSurface)
|
||||
m_wlSurface->unassign();
|
||||
}
|
||||
|
||||
eViewType CPopup::type() const {
|
||||
return VIEW_TYPE_POPUP;
|
||||
}
|
||||
|
||||
bool CPopup::visible() const {
|
||||
if ((!m_mapped || !m_wlSurface->resource()) && (!m_fadingOut || m_alpha->value() > 0.F))
|
||||
return false;
|
||||
|
||||
if (!m_windowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_windowOwner.lock());
|
||||
|
||||
if (!m_layerOwner.expired())
|
||||
return true;
|
||||
|
||||
if (m_parent)
|
||||
return m_parent->visible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<CBox> CPopup::logicalBox() const {
|
||||
return surfaceLogicalBox();
|
||||
}
|
||||
|
||||
std::optional<CBox> CPopup::surfaceLogicalBox() const {
|
||||
if (!visible())
|
||||
return std::nullopt;
|
||||
|
||||
return CBox{coordsGlobal(), size()};
|
||||
}
|
||||
|
||||
bool CPopup::desktopComponent() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPopup::initAllSignals() {
|
||||
|
||||
g_pAnimationManager->createAnimation(0.f, m_alpha, g_pConfigManager->getAnimationPropertyConfig("fadePopupsIn"), AVARDAMAGE_NONE);
|
||||
|
|
@ -129,7 +82,7 @@ void CPopup::initAllSignals() {
|
|||
m_listeners.map = m_resource->m_surface->m_events.map.listen([this] { this->onMap(); });
|
||||
m_listeners.unmap = m_resource->m_surface->m_events.unmap.listen([this] { this->onUnmap(); });
|
||||
m_listeners.dismissed = m_resource->m_events.dismissed.listen([this] { this->onUnmap(); });
|
||||
m_listeners.destroy = m_resource->m_events.destroy.listen([this] { this->onDestroy(); });
|
||||
m_listeners.destroy = m_resource->m_surface->m_events.destroy.listen([this] { this->onDestroy(); });
|
||||
m_listeners.commit = m_resource->m_surface->m_events.commit.listen([this] { this->onCommit(); });
|
||||
m_listeners.newPopup = m_resource->m_surface->m_events.newPopup.listen([this](const auto& resource) { this->onNewPopup(resource); });
|
||||
}
|
||||
|
|
@ -137,7 +90,7 @@ void CPopup::initAllSignals() {
|
|||
void CPopup::onNewPopup(SP<CXDGPopupResource> popup) {
|
||||
const auto& POPUP = m_children.emplace_back(CPopup::create(popup, m_self));
|
||||
POPUP->m_self = POPUP;
|
||||
Log::logger->log(Log::DEBUG, "New popup at {:x}", rc<uintptr_t>(this));
|
||||
Debug::log(LOG, "New popup at {:x}", rc<uintptr_t>(this));
|
||||
}
|
||||
|
||||
void CPopup::onDestroy() {
|
||||
|
|
@ -150,13 +103,8 @@ void CPopup::onDestroy() {
|
|||
m_children.clear();
|
||||
m_wlSurface.reset();
|
||||
|
||||
m_listeners.map.reset();
|
||||
m_listeners.unmap.reset();
|
||||
m_listeners.commit.reset();
|
||||
m_listeners.newPopup.reset();
|
||||
|
||||
if (m_fadingOut && m_alpha->isBeingAnimated()) {
|
||||
Log::logger->log(Log::DEBUG, "popup {:x}: skipping full destroy, animating", rc<uintptr_t>(this));
|
||||
Debug::log(LOG, "popup {:x}: skipping full destroy, animating", rc<uintptr_t>(this));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +112,7 @@ void CPopup::onDestroy() {
|
|||
}
|
||||
|
||||
void CPopup::fullyDestroy() {
|
||||
Log::logger->log(Log::DEBUG, "popup {:x} fully destroying", rc<uintptr_t>(this));
|
||||
Debug::log(LOG, "popup {:x} fully destroying", rc<uintptr_t>(this));
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
std::erase_if(g_pHyprOpenGL->m_popupFramebuffers, [&](const auto& other) { return other.first.expired() || other.first == m_self; });
|
||||
|
|
@ -203,7 +151,7 @@ void CPopup::onMap() {
|
|||
m_alpha->setValueAndWarp(0.F);
|
||||
*m_alpha = 1.F;
|
||||
|
||||
Log::logger->log(Log::DEBUG, "popup {:x}: mapped", rc<uintptr_t>(this));
|
||||
Debug::log(LOG, "popup {:x}: mapped", rc<uintptr_t>(this));
|
||||
}
|
||||
|
||||
void CPopup::onUnmap() {
|
||||
|
|
@ -211,12 +159,12 @@ void CPopup::onUnmap() {
|
|||
return;
|
||||
|
||||
if (!m_resource || !m_resource->m_surface) {
|
||||
Log::logger->log(Log::ERR, "CPopup: orphaned (no surface/resource) and unmaps??");
|
||||
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and unmaps??");
|
||||
onDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
Log::logger->log(Log::DEBUG, "popup {:x}: unmapped", rc<uintptr_t>(this));
|
||||
Debug::log(LOG, "popup {:x}: unmapped", rc<uintptr_t>(this));
|
||||
|
||||
// if the popup committed a different size right now, we also need to damage the old size.
|
||||
const Vector2D MAX_DAMAGE_SIZE = {std::max(m_lastSize.x, m_resource->m_surface->m_surface->m_current.size.x),
|
||||
|
|
@ -271,7 +219,7 @@ void CPopup::onUnmap() {
|
|||
|
||||
void CPopup::onCommit(bool ignoreSiblings) {
|
||||
if (!m_resource || !m_resource->m_surface) {
|
||||
Log::logger->log(Log::ERR, "CPopup: orphaned (no surface/resource) and commits??");
|
||||
Debug::log(ERR, "CPopup: orphaned (no surface/resource) and commits??");
|
||||
onDestroy();
|
||||
return;
|
||||
}
|
||||
|
|
@ -286,7 +234,7 @@ void CPopup::onCommit(bool ignoreSiblings) {
|
|||
|
||||
static auto PLOGDAMAGE = CConfigValue<Hyprlang::INT>("debug:log_damage");
|
||||
if (*PLOGDAMAGE)
|
||||
Log::logger->log(Log::DEBUG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowOwner.lock());
|
||||
Debug::log(LOG, "Refusing to commit damage from a subsurface of {} because it's invisible.", m_windowOwner.lock());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +266,7 @@ void CPopup::onCommit(bool ignoreSiblings) {
|
|||
}
|
||||
|
||||
void CPopup::onReposition() {
|
||||
Log::logger->log(Log::DEBUG, "Popup {:x} requests reposition", rc<uintptr_t>(this));
|
||||
Debug::log(LOG, "Popup {:x} requests reposition", rc<uintptr_t>(this));
|
||||
|
||||
m_requestedReposition = true;
|
||||
|
||||
|
|
@ -338,14 +286,14 @@ void CPopup::reposition() {
|
|||
m_resource->applyPositioning(box, COORDS);
|
||||
}
|
||||
|
||||
SP<Desktop::View::CWLSurface> CPopup::getT1Owner() const {
|
||||
SP<CWLSurface> CPopup::getT1Owner() {
|
||||
if (m_windowOwner)
|
||||
return m_windowOwner->wlSurface();
|
||||
return m_windowOwner->m_wlSurface;
|
||||
else
|
||||
return m_layerOwner->wlSurface();
|
||||
return m_layerOwner->m_surface;
|
||||
}
|
||||
|
||||
Vector2D CPopup::coordsRelativeToParent() const {
|
||||
Vector2D CPopup::coordsRelativeToParent() {
|
||||
Vector2D offset;
|
||||
|
||||
if (!m_resource)
|
||||
|
|
@ -356,7 +304,7 @@ Vector2D CPopup::coordsRelativeToParent() const {
|
|||
|
||||
while (current->m_parent && current->m_resource) {
|
||||
|
||||
offset += current->wlSurface()->resource()->m_current.offset;
|
||||
offset += current->m_wlSurface->resource()->m_current.offset;
|
||||
offset += current->m_resource->m_geometry.pos();
|
||||
|
||||
current = current->m_parent;
|
||||
|
|
@ -365,15 +313,15 @@ Vector2D CPopup::coordsRelativeToParent() const {
|
|||
return offset;
|
||||
}
|
||||
|
||||
Vector2D CPopup::coordsGlobal() const {
|
||||
Vector2D CPopup::coordsGlobal() {
|
||||
return localToGlobal(coordsRelativeToParent());
|
||||
}
|
||||
|
||||
Vector2D CPopup::localToGlobal(const Vector2D& rel) const {
|
||||
Vector2D CPopup::localToGlobal(const Vector2D& rel) {
|
||||
return t1ParentCoords() + rel;
|
||||
}
|
||||
|
||||
Vector2D CPopup::t1ParentCoords() const {
|
||||
Vector2D CPopup::t1ParentCoords() {
|
||||
if (!m_windowOwner.expired())
|
||||
return m_windowOwner->m_realPosition->value();
|
||||
if (!m_layerOwner.expired())
|
||||
|
|
@ -399,32 +347,41 @@ void CPopup::recheckChildrenRecursive() {
|
|||
std::vector<WP<CPopup>> cpy;
|
||||
std::ranges::for_each(m_children, [&cpy](const auto& el) { cpy.emplace_back(el); });
|
||||
for (auto const& c : cpy) {
|
||||
if (!c->visible())
|
||||
continue;
|
||||
c->onCommit(true);
|
||||
c->recheckChildrenRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
Vector2D CPopup::size() const {
|
||||
Vector2D CPopup::size() {
|
||||
return m_lastSize;
|
||||
}
|
||||
|
||||
void CPopup::sendScale() {
|
||||
if (!m_windowOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_windowOwner->wlSurface()->m_lastScaleFloat);
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_windowOwner->m_wlSurface->m_lastScaleFloat);
|
||||
else if (!m_layerOwner.expired())
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_layerOwner->wlSurface()->m_lastScaleFloat);
|
||||
g_pCompositor->setPreferredScaleForSurface(m_wlSurface->resource(), m_layerOwner->m_surface->m_lastScaleFloat);
|
||||
else
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CPopup::bfHelper(std::vector<SP<CPopup>> const& nodes, std::function<void(SP<CPopup>, void*)> fn, void* data) {
|
||||
bool CPopup::visible() {
|
||||
if (!m_windowOwner.expired())
|
||||
return g_pHyprRenderer->shouldRenderWindow(m_windowOwner.lock());
|
||||
if (!m_layerOwner.expired())
|
||||
return true;
|
||||
if (m_parent)
|
||||
return m_parent->visible();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPopup::bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(WP<CPopup>, void*)> fn, void* data) {
|
||||
for (auto const& n : nodes) {
|
||||
fn(n, data);
|
||||
}
|
||||
|
||||
std::vector<SP<CPopup>> nodes2;
|
||||
std::vector<WP<CPopup>> nodes2;
|
||||
nodes2.reserve(nodes.size() * 2);
|
||||
|
||||
for (auto const& n : nodes) {
|
||||
|
|
@ -432,7 +389,7 @@ void CPopup::bfHelper(std::vector<SP<CPopup>> const& nodes, std::function<void(S
|
|||
continue;
|
||||
|
||||
for (auto const& c : n->m_children) {
|
||||
nodes2.emplace_back(c->m_self.lock());
|
||||
nodes2.push_back(c->m_self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -440,18 +397,18 @@ void CPopup::bfHelper(std::vector<SP<CPopup>> const& nodes, std::function<void(S
|
|||
bfHelper(nodes2, fn, data);
|
||||
}
|
||||
|
||||
void CPopup::breadthfirst(std::function<void(SP<CPopup>, void*)> fn, void* data) {
|
||||
void CPopup::breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data) {
|
||||
if (!m_self)
|
||||
return;
|
||||
|
||||
std::vector<SP<CPopup>> popups;
|
||||
popups.emplace_back(m_self.lock());
|
||||
std::vector<WP<CPopup>> popups;
|
||||
popups.push_back(m_self);
|
||||
bfHelper(popups, fn, data);
|
||||
}
|
||||
|
||||
SP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
||||
std::vector<SP<CPopup>> popups;
|
||||
breadthfirst([&popups](SP<CPopup> popup, void* data) { popups.push_back(popup); }, &popups);
|
||||
WP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
||||
std::vector<WP<CPopup>> popups;
|
||||
breadthfirst([&popups](WP<CPopup> popup, void* data) { popups.push_back(popup); }, &popups);
|
||||
|
||||
for (auto const& p : popups | std::views::reverse) {
|
||||
if (!p->m_resource || !p->m_mapped)
|
||||
|
|
@ -470,7 +427,7 @@ SP<CPopup> CPopup::at(const Vector2D& globalCoords, bool allowsInput) {
|
|||
if (BOX.containsPoint(globalCoords))
|
||||
return p;
|
||||
} else {
|
||||
const auto REGION = CRegion{p->wlSurface()->resource()->m_current.input}.intersect(CBox{{}, p->wlSurface()->resource()->m_current.size}).translate(p->coordsGlobal());
|
||||
const auto REGION = CRegion{p->m_wlSurface->resource()->m_current.input}.intersect(CBox{{}, p->m_wlSurface->resource()->m_current.size}).translate(p->coordsGlobal());
|
||||
if (REGION.containsPoint(globalCoords))
|
||||
return p;
|
||||
}
|
||||
|
|
@ -483,7 +440,7 @@ bool CPopup::inert() const {
|
|||
return m_inert;
|
||||
}
|
||||
|
||||
PHLMONITOR CPopup::getMonitor() const {
|
||||
PHLMONITOR CPopup::getMonitor() {
|
||||
if (!m_windowOwner.expired())
|
||||
return m_windowOwner->m_monitor.lock();
|
||||
if (!m_layerOwner.expired())
|
||||
96
src/desktop/Popup.hpp
Normal file
96
src/desktop/Popup.hpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "Subsurface.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include "../helpers/AnimatedVariable.hpp"
|
||||
|
||||
class CXDGPopupResource;
|
||||
|
||||
class CPopup {
|
||||
public:
|
||||
// dummy head nodes
|
||||
static UP<CPopup> create(PHLWINDOW pOwner);
|
||||
static UP<CPopup> create(PHLLS pOwner);
|
||||
|
||||
// real nodes
|
||||
static UP<CPopup> create(SP<CXDGPopupResource> popup, WP<CPopup> pOwner);
|
||||
|
||||
~CPopup();
|
||||
|
||||
SP<CWLSurface> getT1Owner();
|
||||
Vector2D coordsRelativeToParent();
|
||||
Vector2D coordsGlobal();
|
||||
PHLMONITOR getMonitor();
|
||||
|
||||
Vector2D size();
|
||||
|
||||
void onNewPopup(SP<CXDGPopupResource> popup);
|
||||
void onDestroy();
|
||||
void onMap();
|
||||
void onUnmap();
|
||||
void onCommit(bool ignoreSiblings = false);
|
||||
void onReposition();
|
||||
|
||||
void recheckTree();
|
||||
|
||||
bool visible();
|
||||
bool inert() const;
|
||||
|
||||
// will also loop over this node
|
||||
void breadthfirst(std::function<void(WP<CPopup>, void*)> fn, void* data);
|
||||
WP<CPopup> at(const Vector2D& globalCoords, bool allowsInput = false);
|
||||
|
||||
//
|
||||
SP<CWLSurface> m_wlSurface;
|
||||
WP<CPopup> m_self;
|
||||
bool m_mapped = false;
|
||||
|
||||
// fade in-out
|
||||
PHLANIMVAR<float> m_alpha;
|
||||
bool m_fadingOut = false;
|
||||
|
||||
private:
|
||||
CPopup() = default;
|
||||
|
||||
// T1 owners, each popup has to have one of these
|
||||
PHLWINDOWREF m_windowOwner;
|
||||
PHLLSREF m_layerOwner;
|
||||
|
||||
// T2 owners
|
||||
WP<CPopup> m_parent;
|
||||
|
||||
WP<CXDGPopupResource> m_resource;
|
||||
|
||||
Vector2D m_lastSize = {};
|
||||
Vector2D m_lastPos = {};
|
||||
|
||||
bool m_requestedReposition = false;
|
||||
|
||||
bool m_inert = false;
|
||||
|
||||
//
|
||||
std::vector<UP<CPopup>> m_children;
|
||||
UP<CSubsurface> m_subsurfaceHead;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener newPopup;
|
||||
CHyprSignalListener destroy;
|
||||
CHyprSignalListener map;
|
||||
CHyprSignalListener unmap;
|
||||
CHyprSignalListener commit;
|
||||
CHyprSignalListener dismissed;
|
||||
CHyprSignalListener reposition;
|
||||
} m_listeners;
|
||||
|
||||
void initAllSignals();
|
||||
void reposition();
|
||||
void recheckChildrenRecursive();
|
||||
void sendScale();
|
||||
void fullyDestroy();
|
||||
|
||||
Vector2D localToGlobal(const Vector2D& rel);
|
||||
Vector2D t1ParentCoords();
|
||||
static void bfHelper(std::vector<WP<CPopup>> const& nodes, std::function<void(WP<CPopup>, void*)> fn, void* data);
|
||||
};
|
||||
22
src/desktop/Rule.cpp
Normal file
22
src/desktop/Rule.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include <re2/re2.h>
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include "Rule.hpp"
|
||||
#include "../debug/Log.hpp"
|
||||
|
||||
CRuleRegexContainer::CRuleRegexContainer(const std::string& regex_) {
|
||||
const bool NEGATIVE = regex_.starts_with("negative:");
|
||||
|
||||
m_negative = NEGATIVE;
|
||||
m_regex = makeUnique<RE2>(NEGATIVE ? regex_.substr(9) : regex_);
|
||||
|
||||
// TODO: maybe pop an error?
|
||||
if (!m_regex->ok())
|
||||
Debug::log(ERR, "RuleRegexContainer: regex {} failed to parse!", regex_);
|
||||
}
|
||||
|
||||
bool CRuleRegexContainer::passes(const std::string& str) const {
|
||||
if (!m_regex)
|
||||
return false;
|
||||
|
||||
return RE2::FullMatch(str, *m_regex) != m_negative;
|
||||
}
|
||||
21
src/desktop/Rule.hpp
Normal file
21
src/desktop/Rule.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
|
||||
//NOLINTNEXTLINE
|
||||
namespace re2 {
|
||||
class RE2;
|
||||
};
|
||||
|
||||
class CRuleRegexContainer {
|
||||
public:
|
||||
CRuleRegexContainer() = default;
|
||||
|
||||
CRuleRegexContainer(const std::string& regex);
|
||||
|
||||
bool passes(const std::string& str) const;
|
||||
|
||||
private:
|
||||
Hyprutils::Memory::CUniquePointer<re2::RE2> m_regex;
|
||||
bool m_negative = false;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue