mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-30 03:13:48 -07:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4520b30d49 | ||
|
8405aa111d | ||
|
ad46257cce | ||
|
0cfbe618b5 | ||
|
7d0944acdf | ||
|
b9990468f3 | ||
|
736a8735b3 | ||
|
b54eecbd79 | ||
|
657aeb3946 | ||
|
6807430b18 |
101
.clang-tidy
101
.clang-tidy
@@ -1,101 +0,0 @@
|
||||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '.*\.hpp'
|
||||
FormatStyle: file
|
||||
Checks: >
|
||||
-*,
|
||||
bugprone-*,
|
||||
-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-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-unused-parameters,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-include-cleaner,
|
||||
-misc-use-anonymous-namespace,
|
||||
-misc-const-correctness,
|
||||
modernize-*,
|
||||
-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-avoid-endl,
|
||||
-performance-unnecessary-value-param,
|
||||
portability-std-allocator-const,
|
||||
readability-*,
|
||||
-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,
|
||||
|
||||
CheckOptions:
|
||||
performance-for-range-copy.WarnOnAllAutoCopies: true
|
||||
performance-inefficient-string-concatenation.StrictMode: true
|
||||
readability-braces-around-statements.ShortStatementLines: 0
|
||||
readability-identifier-naming.ClassCase: CamelCase
|
||||
readability-identifier-naming.ClassIgnoredRegexp: I.*
|
||||
readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!?
|
||||
readability-identifier-naming.EnumCase: CamelCase
|
||||
readability-identifier-naming.EnumPrefix: e
|
||||
readability-identifier-naming.EnumConstantCase: UPPER_CASE
|
||||
readability-identifier-naming.FunctionCase: camelBack
|
||||
readability-identifier-naming.NamespaceCase: CamelCase
|
||||
readability-identifier-naming.NamespacePrefix: N
|
||||
readability-identifier-naming.StructPrefix: S
|
||||
readability-identifier-naming.StructCase: CamelCase
|
76
.github/ISSUE_TEMPLATE/bug.yml
vendored
76
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -1,15 +1,75 @@
|
||||
name: Do not open issues, go to discussions please!
|
||||
description: Do not open an issue
|
||||
name: Bug Report
|
||||
description: Something is not working right
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Please close this issue.
|
||||
description: Users cannot open issues. I want my issue to be closed.
|
||||
label: Already reported ? *
|
||||
description: Before opening a new bug report, please take a moment to search through the current open and closed issues to check if it already exists.
|
||||
options:
|
||||
- label: Yes, I want this issue to be closed.
|
||||
required: true
|
||||
- label: I have searched the existing open and closed issues.
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: type
|
||||
attributes:
|
||||
label: Regression?
|
||||
description: |
|
||||
Regression means that something used to work but no longer does.
|
||||
**BEFORE CONTINUING**, please check if this bug is a regression or not, and if it is, we need you to bisect with the help of the wiki: https://wiki.hyprland.org/Crashes-and-Bugs/#bisecting-an-issue
|
||||
multiple: true
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: body
|
||||
id: ver
|
||||
attributes:
|
||||
label: Issue body
|
||||
label: System Info and Version
|
||||
description: |
|
||||
Paste the output of `hyprctl systeminfo -c` here. If you can't
|
||||
launch Hyprland, paste the output of `Hyprland --systeminfo`.
|
||||
If `Hyprland --systeminfo` errors out (added in 0.44.0), find
|
||||
and paste the Hyprland version manually.
|
||||
value: "<details>
|
||||
<summary>System/Version info</summary>
|
||||
|
||||
|
||||
```sh
|
||||
|
||||
<Paste the output of the command here>
|
||||
|
||||
```
|
||||
|
||||
|
||||
</details>"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Description
|
||||
description: "What went wrong?"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: How to reproduce
|
||||
description: "How can someone else reproduce the issue?"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Crash reports, logs, images, videos
|
||||
description: |
|
||||
Anything that can help. Please always ATTACH and not paste them.
|
||||
Logs can be found in $XDG_RUNTIME_DIR/hypr
|
||||
Crash reports are stored in ~/.cache/hyprland or $XDG_CACHE_HOME/hyprland
|
||||
|
||||
|
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Feature Request
|
||||
description: I'd like to request additional functionality
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before opening a new issue, take a moment to search through the current open ones.
|
||||
|
||||
---
|
||||
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Description
|
||||
description: "Describe your idea"
|
||||
validations:
|
||||
required: true
|
||||
|
12
.github/actions/setup_base/action.yml
vendored
12
.github/actions/setup_base/action.yml
vendored
@@ -20,7 +20,6 @@ runs:
|
||||
clang \
|
||||
cmake \
|
||||
git \
|
||||
glaze \
|
||||
glm \
|
||||
glslang \
|
||||
go \
|
||||
@@ -34,10 +33,7 @@ runs:
|
||||
libfontenc \
|
||||
libglvnd \
|
||||
libinput \
|
||||
libjxl \
|
||||
libliftoff \
|
||||
libspng \
|
||||
libwebp \
|
||||
libxcursor \
|
||||
libxcvt \
|
||||
libxfont2 \
|
||||
@@ -62,8 +58,7 @@ runs:
|
||||
xcb-util \
|
||||
xcb-util-image \
|
||||
libzip \
|
||||
librsvg \
|
||||
re2
|
||||
librsvg
|
||||
|
||||
- name: Get hyprwayland-scanner-git
|
||||
shell: bash
|
||||
@@ -74,11 +69,6 @@ runs:
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
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 hyprutils-git
|
||||
shell: bash
|
||||
run: |
|
||||
|
6
.github/pull_request_template.md
vendored
6
.github/pull_request_template.md
vendored
@@ -1,9 +1,3 @@
|
||||
<!--
|
||||
BEFORE you submit your PR, please check out the PR guidelines
|
||||
on our wiki: https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/
|
||||
-->
|
||||
|
||||
|
||||
#### Describe your PR, what does it fix/add?
|
||||
|
||||
|
||||
|
7
.github/workflows/ci.yaml
vendored
7
.github/workflows/ci.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Build Hyprland
|
||||
run: |
|
||||
CFLAGS=-Werror CXXFLAGS=-Werror make all
|
||||
make all
|
||||
|
||||
- name: Compress and package artifacts
|
||||
run: |
|
||||
@@ -36,10 +36,10 @@ jobs:
|
||||
cp build/Hyprland hyprland/
|
||||
cp -r example/ hyprland/
|
||||
cp -r assets/ hyprland/
|
||||
tar -cvJf Hyprland.tar.xz hyprland
|
||||
tar -cvf Hyprland.tar.xz hyprland
|
||||
|
||||
- name: Release
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Build archive
|
||||
path: Hyprland.tar.xz
|
||||
@@ -107,7 +107,6 @@ jobs:
|
||||
run: make release
|
||||
|
||||
clang-format:
|
||||
permissions: read-all
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Code Style (Arch)"
|
||||
runs-on: ubuntu-latest
|
||||
|
48
.github/workflows/clang-format.yml
vendored
48
.github/workflows/clang-format.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: clang-format
|
||||
on: pull_request_target
|
||||
jobs:
|
||||
clang-format:
|
||||
permissions: write-all
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||
name: "Code Style (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: clang-format check
|
||||
run: ninja -C build clang-format-check
|
||||
|
||||
- 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), 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: Comment patch
|
||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||
uses: mshick/add-pr-comment@v2
|
||||
with:
|
||||
message-path: |
|
||||
clang-format.patch
|
101
.github/workflows/close-issues.yml
vendored
101
.github/workflows/close-issues.yml
vendored
@@ -1,101 +0,0 @@
|
||||
name: Close Unauthorized Issues
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
close-unauthorized-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
# XXX: This *could* be done in Bash by abusing GitHub's own tool to interact with its API
|
||||
# but that's too much of a hack, and we'll be adding a layer of abstraction. github-script
|
||||
# is a workflow that eases interaction with GitHub API in the workflow run context.
|
||||
- name: "Close 'unauthorized' issues"
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const ALLOWED_USERS = ['vaxerski', 'fufexan', 'NotAShelf'];
|
||||
const CLOSING_COMMENT = 'Users are no longer allowed to open issues themselves, please open a discussion instead.\n\nPlease see the [wiki](https://wiki.hyprland.org/Contributing-and-Debugging/Issue-Guidelines/) on why this is the case.\n\nWe are volunteers, and we need your cooperation to make the best software we can. Thank you for understanding! ❤️\n\n[Open a discussion here](https://github.com/hyprwm/Hyprland/discussions)';
|
||||
|
||||
async function closeUnauthorizedIssue(issueNumber, userName) {
|
||||
if (ALLOWED_USERS.includes(userName)) {
|
||||
console.log(`Issue #${issueNumber} - Created by authorized user ${userName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Issue #${issueNumber} - Unauthorized, closing`);
|
||||
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
state: 'closed',
|
||||
state_reason: 'not_planned'
|
||||
});
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
body: CLOSING_COMMENT
|
||||
});
|
||||
}
|
||||
|
||||
if (context.eventName === 'issues' && context.payload.action === 'opened') {
|
||||
// Direct access to the issue that triggered the workflow
|
||||
const issue = context.payload.issue;
|
||||
|
||||
// Skip if this is a PR
|
||||
if (issue.pull_request) {
|
||||
console.log(`Issue #${issue.number} - Skipping, this is a pull request`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the single issue that triggered the workflow
|
||||
await closeUnauthorizedIssue(issue.number, issue.user.login);
|
||||
} else {
|
||||
// For manual runs, we need to handle pagination
|
||||
async function* fetchAllOpenIssues() {
|
||||
let page = 1;
|
||||
let hasNextPage = true;
|
||||
|
||||
while (hasNextPage) {
|
||||
const response = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
page: page
|
||||
});
|
||||
|
||||
if (response.data.length === 0) {
|
||||
hasNextPage = false;
|
||||
} else {
|
||||
for (const issue of response.data) {
|
||||
yield issue;
|
||||
}
|
||||
page++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process issues one by one
|
||||
for await (const issue of fetchAllOpenIssues()) {
|
||||
try {
|
||||
// Skip pull requests
|
||||
if (issue.pull_request) {
|
||||
console.log(`Issue #${issue.number} - Skipping, this is a pull request`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await closeUnauthorizedIssue(issue.number, issue.user.login);
|
||||
} catch (error) {
|
||||
console.error(`Error processing issue #${issue.number}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
4
.github/workflows/man-update.yaml
vendored
4
.github/workflows/man-update.yaml
vendored
@@ -17,14 +17,14 @@ jobs:
|
||||
run: sudo apt install pandoc
|
||||
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.PAT }}
|
||||
|
||||
- name: Build man pages
|
||||
run: make man
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
name: Commit
|
||||
with:
|
||||
commit_message: "[gha] build man pages"
|
||||
|
28
.github/workflows/nix-build.yml
vendored
Normal file
28
.github/workflows/nix-build.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Nix (Build)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
package:
|
||||
- hyprland
|
||||
- hyprland-cross
|
||||
- xdg-desktop-portal-hyprland
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- uses: cachix/cachix-action@v15
|
||||
with:
|
||||
name: hyprland
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- run: nix build 'github:hyprwm/Hyprland?ref=${{ github.ref }}#${{ matrix.package }}' -L --extra-substituters "https://hyprland.cachix.org"
|
22
.github/workflows/nix-ci.yml
vendored
22
.github/workflows/nix-ci.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Nix
|
||||
name: Nix (CI)
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
@@ -8,23 +8,7 @@ jobs:
|
||||
uses: ./.github/workflows/nix-update-inputs.yml
|
||||
secrets: inherit
|
||||
|
||||
hyprland:
|
||||
build:
|
||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
|
||||
uses: ./.github/workflows/nix.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
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)
|
||||
needs: hyprland
|
||||
uses: ./.github/workflows/nix.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
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
|
||||
uses: ./.github/workflows/nix-build.yml
|
||||
secrets: inherit
|
||||
|
59
.github/workflows/nix-test.yml
vendored
59
.github/workflows/nix-test.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: Nix (Test)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: nixbuild/nix-quick-install-action@v31
|
||||
with:
|
||||
nix_conf: |
|
||||
keep-env-derivations = true
|
||||
keep-outputs = true
|
||||
|
||||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}
|
||||
# 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:
|
||||
name: hyprland
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Run test VM
|
||||
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
|
||||
|
||||
- name: Upload artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs
|
||||
path: result
|
35
.github/workflows/nix-update-inputs.yml
vendored
35
.github/workflows/nix-update-inputs.yml
vendored
@@ -13,44 +13,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.PAT }}
|
||||
|
||||
- name: Install Nix
|
||||
uses: nixbuild/nix-quick-install-action@v31
|
||||
with:
|
||||
nix_conf: |
|
||||
keep-env-derivations = true
|
||||
keep-outputs = true
|
||||
|
||||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
|
||||
# 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: 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
|
||||
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- name: Update inputs
|
||||
run: nix/update-inputs.sh
|
||||
|
||||
- name: Commit
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: "[gha] Nix: update inputs"
|
||||
|
53
.github/workflows/nix.yml
vendored
53
.github/workflows/nix.yml
vendored
@@ -1,53 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
command:
|
||||
required: true
|
||||
type: string
|
||||
description: Command to run
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Nix
|
||||
uses: nixbuild/nix-quick-install-action@v31
|
||||
with:
|
||||
nix_conf: |
|
||||
keep-env-derivations = true
|
||||
keep-outputs = true
|
||||
|
||||
- name: Restore and save Nix store
|
||||
uses: nix-community/cache-nix-action@v6
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}
|
||||
# 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:
|
||||
name: hyprland
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- run: ${{ inputs.command }}
|
3
.github/workflows/release.yaml
vendored
3
.github/workflows/release.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout Hyprland
|
||||
id: checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
run: |
|
||||
git fetch --unshallow || echo "failed unshallowing"
|
||||
bash -c scripts/generateVersion.sh
|
||||
mv scripts/generateVersion.sh scripts/generateVersion.sh.bak
|
||||
|
||||
- name: Create tarball with submodules
|
||||
id: tar
|
||||
|
2
.github/workflows/security-checks.yml
vendored
2
.github/workflows/security-checks.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Scan with Flawfinder
|
||||
uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c
|
||||
|
28
.github/workflows/stale.yml
vendored
Normal file
28
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "7 */4 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository == 'hyprwm/Hyprland'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.STALEBOT_PAT }}
|
||||
stale-issue-label: "stale"
|
||||
stale-pr-label: "stale"
|
||||
operations-per-run: 40
|
||||
days-before-close: -1
|
6
.gitignore
vendored
6
.gitignore
vendored
@@ -14,7 +14,6 @@ _deps
|
||||
|
||||
build/
|
||||
result*
|
||||
/.pre-commit-config.yaml
|
||||
/.vscode/
|
||||
/.idea/
|
||||
.envrc
|
||||
@@ -28,8 +27,6 @@ protocols/*.c*
|
||||
protocols/*.h*
|
||||
.ccls-cache
|
||||
*.so
|
||||
src/render/shaders/*.inc
|
||||
src/render/shaders/Shaders.hpp
|
||||
|
||||
hyprctl/hyprctl
|
||||
|
||||
@@ -42,6 +39,3 @@ PKGBUILD
|
||||
src/version.h
|
||||
hyprpm/Makefile
|
||||
hyprctl/Makefile
|
||||
|
||||
**/.#*.*
|
||||
**/#*.*#
|
||||
|
122
CMakeLists.txt
122
CMakeLists.txt
@@ -9,14 +9,12 @@ project(
|
||||
DESCRIPTION "A Modern C++ Wayland Compositor"
|
||||
VERSION ${VER})
|
||||
|
||||
include(CTest)
|
||||
include(CheckIncludeFile)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
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")
|
||||
@@ -26,9 +24,6 @@ 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})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
@@ -76,23 +71,17 @@ else()
|
||||
message(STATUS "Configuring Hyprland in Release with CMake")
|
||||
endif()
|
||||
|
||||
add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
|
||||
|
||||
include_directories(. "src/" "protocols/")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CXX_STANDARD_REQUIRED ON)
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wno-unused-parameter
|
||||
-Wno-unused-value
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-gnu-zero-variadic-macro-arguments
|
||||
-Wno-narrowing
|
||||
-Wno-pointer-arith
|
||||
-Wno-clobbered
|
||||
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
|
||||
|
||||
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
|
||||
@@ -102,28 +91,18 @@ message(STATUS "Checking deps...")
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
set(GLES_VERSION "GLES3")
|
||||
if(LEGACY_RENDERER)
|
||||
set(GLES_VERSION "GLES2")
|
||||
else()
|
||||
set(GLES_VERSION "GLES3")
|
||||
endif()
|
||||
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
||||
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.3)
|
||||
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
|
||||
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
|
||||
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.8.2)
|
||||
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.3)
|
||||
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.1)
|
||||
|
||||
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)
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine)
|
||||
|
||||
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
|
||||
@@ -132,17 +111,19 @@ pkg_check_modules(
|
||||
xkbcommon
|
||||
uuid
|
||||
wayland-server>=1.22.90
|
||||
wayland-protocols>=1.45
|
||||
wayland-protocols
|
||||
cairo
|
||||
pango
|
||||
pangocairo
|
||||
pixman-1
|
||||
xcursor
|
||||
libdrm
|
||||
libinput>=1.28
|
||||
libinput
|
||||
gbm
|
||||
gio-2.0
|
||||
re2)
|
||||
hyprlang>=0.3.2
|
||||
hyprcursor>=0.1.7
|
||||
hyprutils>=0.2.3)
|
||||
|
||||
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
|
||||
|
||||
@@ -156,7 +137,7 @@ endif()
|
||||
|
||||
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
|
||||
|
||||
set(USE_GPROF OFF)
|
||||
set(USE_GPROF ON)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
message(STATUS "Setting debug flags")
|
||||
@@ -209,10 +190,9 @@ if(NOT HAS_TIMERFD AND epoll_FOUND)
|
||||
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 PkgConfig::inotify)
|
||||
if(LEGACY_RENDERER)
|
||||
message(STATUS "Using the legacy GLES2 renderer!")
|
||||
add_compile_definitions(LEGACY_RENDERER)
|
||||
endif()
|
||||
|
||||
if(NO_XWAYLAND)
|
||||
@@ -239,15 +219,6 @@ if(NO_SYSTEMD)
|
||||
else()
|
||||
message(STATUS "SYSTEMD support is requested (NO_SYSTEMD not defined)...")
|
||||
add_compile_definitions(USES_SYSTEMD)
|
||||
|
||||
# session file -uwsm
|
||||
if(NO_UWSM)
|
||||
message(STATUS "UWSM support is disabled...")
|
||||
else()
|
||||
message(STATUS "UWSM support is enabled (NO_UWSM not defined)...")
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/systemd/hyprland-uwsm.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
@@ -261,15 +232,7 @@ target_precompile_headers(Hyprland PRIVATE
|
||||
|
||||
message(STATUS "Setting link libraries")
|
||||
|
||||
target_link_libraries(
|
||||
Hyprland
|
||||
rt
|
||||
PkgConfig::aquamarine_dep
|
||||
PkgConfig::hyprlang_dep
|
||||
PkgConfig::hyprutils_dep
|
||||
PkgConfig::hyprcursor_dep
|
||||
PkgConfig::hyprgraphics_dep
|
||||
PkgConfig::deps)
|
||||
target_link_libraries(Hyprland rt PkgConfig::aquamarine_dep PkgConfig::deps)
|
||||
if(udis_dep_FOUND)
|
||||
target_link_libraries(Hyprland PkgConfig::udis_dep)
|
||||
else()
|
||||
@@ -311,7 +274,7 @@ endfunction()
|
||||
|
||||
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
|
||||
|
||||
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
|
||||
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.2.0)
|
||||
if(hyprland_protocols_dep_FOUND)
|
||||
pkg_get_variable(HYPRLAND_PROTOCOLS hyprland-protocols pkgdatadir)
|
||||
message(STATUS "hyprland-protocols dependency set to ${HYPRLAND_PROTOCOLS}")
|
||||
@@ -337,15 +300,7 @@ protocolnew("protocols" "kde-server-decoration" true)
|
||||
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
|
||||
protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
|
||||
protocolnew("protocols" "xx-color-management-v4" true)
|
||||
protocolnew("protocols" "frog-color-management-v1" true)
|
||||
protocolnew("protocols" "wayland-drm" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-lock-notify-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-mapping-v1"
|
||||
true)
|
||||
|
||||
protocolnew("staging/tearing-control" "tearing-control-v1" false)
|
||||
protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
|
||||
protocolnew("unstable/xdg-output" "xdg-output-unstable-v1" false)
|
||||
@@ -376,33 +331,12 @@ protocolnew("staging/drm-lease" "drm-lease-v1" false)
|
||||
protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
|
||||
protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
|
||||
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
|
||||
protocolnew("staging/security-context" "security-context-v1" false)
|
||||
protocolnew("staging/content-type" "content-type-v1" false)
|
||||
protocolnew("staging/color-management" "color-management-v1" false)
|
||||
protocolnew("staging/xdg-toplevel-tag" "xdg-toplevel-tag-v1" false)
|
||||
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)
|
||||
|
||||
protocolwayland()
|
||||
|
||||
# tools
|
||||
add_subdirectory(hyprctl)
|
||||
|
||||
if(NO_HYPRPM)
|
||||
message(STATUS "hyprpm is disabled")
|
||||
else()
|
||||
add_subdirectory(hyprpm)
|
||||
message(STATUS "hyprpm is enabled (NO_HYPRPM not defined)")
|
||||
endif()
|
||||
|
||||
if(NO_TESTS)
|
||||
message(STATUS "building tests is disabled")
|
||||
else()
|
||||
message(STATUS "building tests is enabled (NO_TESTS not defined)")
|
||||
add_subdirectory(hyprtester)
|
||||
endif()
|
||||
add_subdirectory(hyprpm)
|
||||
|
||||
# binary and symlink
|
||||
install(TARGETS Hyprland)
|
||||
@@ -413,6 +347,7 @@ install(
|
||||
${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \
|
||||
\"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
|
||||
)")
|
||||
|
||||
# session file
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
|
||||
@@ -456,19 +391,4 @@ install(
|
||||
DIRECTORY ${HEADERS_SRC}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
PATTERN "*.inc")
|
||||
|
||||
if(TESTS)
|
||||
enable_testing()
|
||||
add_custom_target(tests)
|
||||
|
||||
add_subdirectory(hyprtester)
|
||||
add_test(
|
||||
NAME "Main Test"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hyprtester
|
||||
COMMAND hyprtester)
|
||||
|
||||
add_dependencies(tests hyprtester)
|
||||
endif()
|
||||
PATTERN "*.h*")
|
||||
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022-2025, vaxerski
|
||||
Copyright (c) 2022-2024, vaxerski
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
15
Makefile
15
Makefile
@@ -1,7 +1,12 @@
|
||||
PREFIX = /usr/local
|
||||
|
||||
stub:
|
||||
@echo "Do not run $(MAKE) directly without any arguments. Please refer to the wiki on how to compile Hyprland."
|
||||
legacyrenderer:
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./buildZ
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
legacyrendererdebug:
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -DLEGACY_RENDERER:BOOL=true -S . -B ./build
|
||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||
|
||||
release:
|
||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=${PREFIX} -S . -B ./build
|
||||
@@ -44,7 +49,7 @@ installheaders:
|
||||
|
||||
cmake --build ./build --config Release --target generate-protocol-headers
|
||||
|
||||
find src -type f \( -name '*.hpp' -o -name '*.h' -o -name '*.inc' \) -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
|
||||
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
|
||||
cp ./protocols/*.h* ${PREFIX}/include/hyprland/protocols
|
||||
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
|
||||
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
|
||||
@@ -92,7 +97,3 @@ asan:
|
||||
@echo "Hyprland done"
|
||||
|
||||
ASAN_OPTIONS="detect_odr_violation=0,log_path=asan.log" HYPRLAND_NO_CRASHREPORTER=1 ./build/Hyprland -c ~/.config/hypr/hyprland.conf
|
||||
|
||||
test:
|
||||
$(MAKE) debug
|
||||
./build/hyprtester/hyprtester -c hyprtester/test.conf -b ./build/Hyprland -p hyprtester/plugin/hyprtestplugin.so
|
||||
|
17
README.md
17
README.md
@@ -4,7 +4,7 @@
|
||||
|
||||
<br>
|
||||
|
||||
[![Badge Workflow]][Workflow]
|
||||
![Badge Workflow]
|
||||
[![Badge License]][License]
|
||||
![Badge Language]
|
||||
[![Badge Pull Requests]][Pull Requests]
|
||||
@@ -100,7 +100,7 @@ easy IPC, much more QoL stuff than other compositors and more...
|
||||
|
||||
<!----------------------------------------------------------------------------->
|
||||
|
||||
[Configure]: https://wiki.hypr.land/Configuring/
|
||||
[Configure]: https://wiki.hyprland.org/Configuring/Configuring-Hyprland/
|
||||
[Stars]: https://starchart.cc/hyprwm/Hyprland
|
||||
[Hypr]: https://github.com/hyprwm/Hypr
|
||||
|
||||
@@ -108,10 +108,9 @@ easy IPC, much more QoL stuff than other compositors and more...
|
||||
[Issues]: https://github.com/hyprwm/Hyprland/issues
|
||||
[Todo]: https://github.com/hyprwm/Hyprland/projects?type=beta
|
||||
|
||||
[Contribute]: https://wiki.hypr.land/Contributing-and-Debugging/
|
||||
[Install]: https://wiki.hypr.land/Getting-Started/Installation/
|
||||
[Quick Start]: https://wiki.hypr.land/Getting-Started/Master-Tutorial/
|
||||
[Workflow]: https://github.com/hyprwm/Hyprland/actions/workflows/ci.yaml
|
||||
[Contribute]: https://wiki.hyprland.org/Contributing-and-Debugging/
|
||||
[Install]: https://wiki.hyprland.org/Getting-Started/Installation/
|
||||
[Quick Start]: https://wiki.hyprland.org/Getting-Started/Master-Tutorial/
|
||||
[License]: LICENSE
|
||||
|
||||
|
||||
@@ -126,9 +125,9 @@ easy IPC, much more QoL stuff than other compositors and more...
|
||||
|
||||
<!----------------------------------{ Images }--------------------------------->
|
||||
|
||||
[Preview A]: https://i.ibb.co/XxFY75Mk/greerggergerhtrytghjnyhjn.png
|
||||
[Preview B]: https://i.ibb.co/C1yTb0r/falf.png
|
||||
[Preview C]: https://i.ibb.co/2Yc4q835/hyprland-preview-b.png
|
||||
[Preview A]: https://i.ibb.co/C1yTb0r/falf.png
|
||||
[Preview B]: https://linfindel.github.io/cdn/hyprland-preview-b.png
|
||||
[Preview C]: https://i.ibb.co/B3GJg28/20221126-20h53m26s-grim.png
|
||||
|
||||
|
||||
<!----------------------------------{ Badges }--------------------------------->
|
||||
|
32
SECURITY.md
32
SECURITY.md
@@ -1,32 +0,0 @@
|
||||
# Hyprland Development Security Policy
|
||||
|
||||
If you have a bug that affects the security of your system, you may
|
||||
want to privately disclose it instead of making it immediately public.
|
||||
|
||||
## Supported versions
|
||||
|
||||
_Only_ the most recent release on Github is supported. There are no LTS releases.
|
||||
|
||||
## What is not a security issue
|
||||
|
||||
Some examples of issues that should not be reported as security issues:
|
||||
|
||||
- An app can execute a command when ran outside of a sandbox
|
||||
- An app can write / read hyprland sockets when ran outside of a sandbox
|
||||
- Crashes
|
||||
- Things that are protected via permissions when the permission system is disabled
|
||||
|
||||
## What is a security issue
|
||||
|
||||
Some examples of issues that should be reported as security issues:
|
||||
|
||||
- Sandboxed application executing arbitrary code via Hyprland
|
||||
- Application being able to modify Hyprland's code on the fly
|
||||
- Application being able to keylog / track user's activity beyond what the wayland protocols allow
|
||||
|
||||
## How to report security issues
|
||||
|
||||
Please report your security issues via either of these channels:
|
||||
- Mail: `vaxry [at] vaxry [dot] net`
|
||||
- Matrix: `@vaxry:matrix.vaxry.net`
|
||||
- Discord: `@vaxry`
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 506 KiB |
@@ -1,19 +1,5 @@
|
||||
.\" Automatically generated by Pandoc 3.1.3
|
||||
.\" Automatically generated by Pandoc 2.9.2.1
|
||||
.\"
|
||||
.\" Define V font for inline verbatim, using C font in formats
|
||||
.\" that render this, and otherwise B font.
|
||||
.ie "\f[CB]x\f[]"x" \{\
|
||||
. ftr V B
|
||||
. ftr VI BI
|
||||
. ftr VB B
|
||||
. ftr VBI BI
|
||||
.\}
|
||||
.el \{\
|
||||
. ftr V CR
|
||||
. ftr VI CI
|
||||
. ftr VB CB
|
||||
. ftr VBI CBI
|
||||
.\}
|
||||
.TH "Hyprland" "1" "" "" "Hyprland User Manual"
|
||||
.hy
|
||||
.SH NAME
|
||||
|
@@ -2,15 +2,15 @@
|
||||
|
||||
First of all, please remember to:
|
||||
- Check that your issue is not a duplicate
|
||||
- Read the [FAQ](https://wiki.hypr.land/FAQ/)
|
||||
- Read the [Configuring Page](https://wiki.hypr.land/Configuring/)
|
||||
- Read the [FAQ](https://wiki.hyprland.org/FAQ/)
|
||||
- Read the [Configuring Page](https://wiki.hyprland.org/Configuring/Configuring-Hyprland)
|
||||
|
||||
<br/>
|
||||
|
||||
# Reporting suggestions
|
||||
Suggestions are welcome.
|
||||
|
||||
Many features can be implemented using bash scripts and Hyprland sockets, read up on those [Here](https://wiki.hypr.land/IPC). Please do not suggest features that can be implemented as such.
|
||||
Many features can be implemented using bash scripts and Hyprland sockets, read up on those [Here](https://wiki.hyprland.org/IPC). Please do not suggest features that can be implemented as such.
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -70,7 +70,7 @@ A debug coredump provides more information for debugging and may speed up the pr
|
||||
|
||||
Make sure you're on latest git. Run `git pull --recurse-submodules` to sync everything.
|
||||
|
||||
1. [Compile Hyprland with debug mode](http://wiki.hypr.land/Contributing-and-Debugging/#build-in-debug-mode)
|
||||
1. [Compile Hyprland with debug mode](http://wiki.hyprland.org/Contributing-and-Debugging/#build-in-debug-mode)
|
||||
> Note: The config file used will be `hyprlandd.conf` instead of `hyprland.conf`
|
||||
|
||||
2. `cd ~`
|
||||
|
@@ -1,19 +1,5 @@
|
||||
.\" Automatically generated by Pandoc 3.1.3
|
||||
.\" Automatically generated by Pandoc 2.9.2.1
|
||||
.\"
|
||||
.\" Define V font for inline verbatim, using C font in formats
|
||||
.\" that render this, and otherwise B font.
|
||||
.ie "\f[CB]x\f[]"x" \{\
|
||||
. ftr V B
|
||||
. ftr VI BI
|
||||
. ftr VB B
|
||||
. ftr VBI BI
|
||||
.\}
|
||||
.el \{\
|
||||
. ftr V CR
|
||||
. ftr VI CI
|
||||
. ftr VB CB
|
||||
. ftr VBI CBI
|
||||
.\}
|
||||
.TH "hyprctl" "1" "" "" "hyprctl User Manual"
|
||||
.hy
|
||||
.SH NAME
|
||||
|
14
example/hyprland-session.service
Normal file
14
example/hyprland-session.service
Normal file
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=Hyprland - Tiling compositor with the looks
|
||||
Documentation=man:Hyprland(1)
|
||||
BindsTo=graphical-session.target
|
||||
Before=graphical-session.target
|
||||
Wants=graphical-session-pre.target
|
||||
After=graphical-session-pre.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/usr/bin/Hyprland
|
||||
ExecStop=/usr/bin/hyprctl dispatch exit
|
||||
Restart=on-failure
|
||||
Slice=session.slice
|
5
example/hyprland-systemd.desktop
Normal file
5
example/hyprland-systemd.desktop
Normal file
@@ -0,0 +1,5 @@
|
||||
[Desktop Entry]
|
||||
Name=Hyprland
|
||||
Comment=An intelligent dynamic tiling Wayland compositor
|
||||
Exec=systemctl --user start --wait hyprland-session
|
||||
Type=Application
|
@@ -1,6 +1,6 @@
|
||||
# This is an example Hyprland config file.
|
||||
# Refer to the wiki for more information.
|
||||
# https://wiki.hypr.land/Configuring/
|
||||
# https://wiki.hyprland.org/Configuring/Configuring-Hyprland/
|
||||
|
||||
# Please note not all available settings / options are set here.
|
||||
# For a full list, see the wiki
|
||||
@@ -14,7 +14,7 @@
|
||||
### MONITORS ###
|
||||
################
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Monitors/
|
||||
# See https://wiki.hyprland.org/Configuring/Monitors/
|
||||
monitor=,preferred,auto,auto
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ monitor=,preferred,auto,auto
|
||||
### MY PROGRAMS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Keywords/
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
|
||||
# Set programs that you use
|
||||
$terminal = kitty
|
||||
@@ -46,72 +46,52 @@ $menu = wofi --show drun
|
||||
### ENVIRONMENT VARIABLES ###
|
||||
#############################
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Environment-variables/
|
||||
# See https://wiki.hyprland.org/Configuring/Environment-variables/
|
||||
|
||||
env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
|
||||
|
||||
###################
|
||||
### PERMISSIONS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Permissions/
|
||||
# Please note permission changes here require a Hyprland restart and are not applied on-the-fly
|
||||
# for security reasons
|
||||
|
||||
# ecosystem {
|
||||
# enforce_permissions = 1
|
||||
# }
|
||||
|
||||
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
|
||||
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
|
||||
# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow
|
||||
|
||||
|
||||
#####################
|
||||
### LOOK AND FEEL ###
|
||||
#####################
|
||||
|
||||
# Refer to https://wiki.hypr.land/Configuring/Variables/
|
||||
# Refer to https://wiki.hyprland.org/Configuring/Variables/
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#general
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#general
|
||||
general {
|
||||
gaps_in = 5
|
||||
gaps_out = 20
|
||||
|
||||
border_size = 2
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#variable-types for info about colors
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
|
||||
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
|
||||
col.inactive_border = rgba(595959aa)
|
||||
|
||||
# Set to true enable resizing windows by clicking and dragging on borders and gaps
|
||||
resize_on_border = false
|
||||
|
||||
# Please see https://wiki.hypr.land/Configuring/Tearing/ before you turn this on
|
||||
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
|
||||
allow_tearing = false
|
||||
|
||||
layout = dwindle
|
||||
}
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#decoration
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
decoration {
|
||||
rounding = 10
|
||||
rounding_power = 2
|
||||
|
||||
# Change transparency of focused and unfocused windows
|
||||
active_opacity = 1.0
|
||||
inactive_opacity = 1.0
|
||||
|
||||
shadow {
|
||||
enabled = true
|
||||
range = 4
|
||||
render_power = 3
|
||||
color = rgba(1a1a1aee)
|
||||
}
|
||||
drop_shadow = true
|
||||
shadow_range = 4
|
||||
shadow_render_power = 3
|
||||
col.shadow = rgba(1a1a1aee)
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#blur
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#blur
|
||||
blur {
|
||||
enabled = true
|
||||
size = 3
|
||||
@@ -121,61 +101,34 @@ decoration {
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#animations
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#animations
|
||||
animations {
|
||||
enabled = yes, please :)
|
||||
enabled = true
|
||||
|
||||
# Default curves, see https://wiki.hypr.land/Configuring/Animations/#curves
|
||||
# NAME, X0, Y0, X1, Y1
|
||||
bezier = easeOutQuint, 0.23, 1, 0.32, 1
|
||||
bezier = easeInOutCubic, 0.65, 0.05, 0.36, 1
|
||||
bezier = linear, 0, 0, 1, 1
|
||||
bezier = almostLinear, 0.5, 0.5, 0.75, 1
|
||||
bezier = quick, 0.15, 0, 0.1, 1
|
||||
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
|
||||
|
||||
# Default animations, see https://wiki.hypr.land/Configuring/Animations/
|
||||
# NAME, ONOFF, SPEED, CURVE, [STYLE]
|
||||
animation = global, 1, 10, default
|
||||
animation = border, 1, 5.39, easeOutQuint
|
||||
animation = windows, 1, 4.79, easeOutQuint
|
||||
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
|
||||
animation = windowsOut, 1, 1.49, linear, popin 87%
|
||||
animation = fadeIn, 1, 1.73, almostLinear
|
||||
animation = fadeOut, 1, 1.46, almostLinear
|
||||
animation = fade, 1, 3.03, quick
|
||||
animation = layers, 1, 3.81, easeOutQuint
|
||||
animation = layersIn, 1, 4, easeOutQuint, fade
|
||||
animation = layersOut, 1, 1.5, linear, fade
|
||||
animation = fadeLayersIn, 1, 1.79, almostLinear
|
||||
animation = fadeLayersOut, 1, 1.39, almostLinear
|
||||
animation = workspaces, 1, 1.94, almostLinear, fade
|
||||
animation = workspacesIn, 1, 1.21, almostLinear, fade
|
||||
animation = workspacesOut, 1, 1.94, almostLinear, fade
|
||||
animation = zoomFactor, 1, 7, quick
|
||||
bezier = myBezier, 0.05, 0.9, 0.1, 1.05
|
||||
|
||||
animation = windows, 1, 7, myBezier
|
||||
animation = windowsOut, 1, 7, default, popin 80%
|
||||
animation = border, 1, 10, default
|
||||
animation = borderangle, 1, 8, default
|
||||
animation = fade, 1, 7, default
|
||||
animation = workspaces, 1, 6, default
|
||||
}
|
||||
|
||||
# Ref https://wiki.hypr.land/Configuring/Workspace-Rules/
|
||||
# "Smart gaps" / "No gaps when only"
|
||||
# uncomment all if you wish to use that.
|
||||
# workspace = w[tv1], gapsout:0, gapsin:0
|
||||
# workspace = f[1], gapsout:0, gapsin:0
|
||||
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrule = rounding 0, floating:0, onworkspace:f[1]
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||
preserve_split = true # You probably want this
|
||||
}
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Master-Layout/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
|
||||
master {
|
||||
new_status = master
|
||||
}
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#misc
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
||||
misc {
|
||||
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
||||
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
|
||||
@@ -186,7 +139,7 @@ misc {
|
||||
### INPUT ###
|
||||
#############
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#input
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#input
|
||||
input {
|
||||
kb_layout = us
|
||||
kb_variant =
|
||||
@@ -203,11 +156,13 @@ input {
|
||||
}
|
||||
}
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Gestures
|
||||
gesture = 3, horizontal, workspace
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#gestures
|
||||
gestures {
|
||||
workspace_swipe = false
|
||||
}
|
||||
|
||||
# Example per-device config
|
||||
# See https://wiki.hypr.land/Configuring/Keywords/#per-device-input-configs for more
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
|
||||
device {
|
||||
name = epic-mouse-v1
|
||||
sensitivity = -0.5
|
||||
@@ -218,10 +173,10 @@ device {
|
||||
### KEYBINDINGS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Keywords/
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
$mainMod = SUPER # Sets "Windows" key as main modifier
|
||||
|
||||
# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
|
||||
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
|
||||
bind = $mainMod, Q, exec, $terminal
|
||||
bind = $mainMod, C, killactive,
|
||||
bind = $mainMod, M, exit,
|
||||
@@ -278,8 +233,8 @@ bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@
|
||||
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
|
||||
|
||||
# Requires playerctl
|
||||
bindl = , XF86AudioNext, exec, playerctl next
|
||||
@@ -291,14 +246,17 @@ bindl = , XF86AudioPrev, exec, playerctl previous
|
||||
### WINDOWS AND WORKSPACES ###
|
||||
##############################
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
# Example windowrule
|
||||
# windowrule = float,class:^(kitty)$,title:^(kitty)$
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(kitty)$
|
||||
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
windowrule = suppressevent maximize, class:.*
|
||||
windowrulev2 = suppressevent maximize, class:.*
|
||||
|
||||
# Fix some dragging issues with XWayland
|
||||
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
windowrulev2 = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
||||
|
@@ -2,6 +2,4 @@
|
||||
Name=Hyprland
|
||||
Comment=An intelligent dynamic tiling Wayland compositor
|
||||
Exec=Hyprland
|
||||
Type=Application
|
||||
DesktopNames=Hyprland
|
||||
Keywords=tiling;wayland;compositor;
|
||||
Type=Application
|
@@ -22,6 +22,5 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
]
|
||||
}
|
@@ -1,19 +1,16 @@
|
||||
//
|
||||
// Example blue light filter shader.
|
||||
//
|
||||
|
||||
#version 300 es
|
||||
//
|
||||
|
||||
precision mediump float;
|
||||
in vec2 v_texcoord;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 pixColor = texture(tex, v_texcoord);
|
||||
vec4 pixColor = texture2D(tex, v_texcoord);
|
||||
|
||||
pixColor[2] *= 0.8;
|
||||
|
||||
fragColor = pixColor;
|
||||
gl_FragColor = pixColor;
|
||||
}
|
||||
|
192
flake.lock
generated
192
flake.lock
generated
@@ -16,11 +16,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755946532,
|
||||
"narHash": "sha256-POePremlUY5GyA1zfbtic6XLxDaQcqHN6l+bIxdT5gc=",
|
||||
"lastModified": 1727261104,
|
||||
"narHash": "sha256-rxDI7WrxIRV9it9mDCHcLa7xQykf1JloXnoXr5xQ8zI=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "aquamarine",
|
||||
"rev": "81584dae2df6ac79f6b6dae0ecb7705e95129ada",
|
||||
"rev": "b82fdaff917582a9d568969e15e61b398c71e990",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -29,43 +29,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprcursor": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
@@ -79,11 +42,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753964049,
|
||||
"narHash": "sha256-lIqabfBY7z/OANxHoPeIrDJrFyYy9jAM4GQLzZ2feCM=",
|
||||
"lastModified": 1727532803,
|
||||
"narHash": "sha256-ZaZ7h7PY8mQc4vtGmVqWLAq9CAO02gHMyNR5yY8zDmM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprcursor",
|
||||
"rev": "44e91d467bdad8dcf8bbd2ac7cf49972540980a5",
|
||||
"rev": "b98726e431d4d3ed58bd58bee1047cdb81cec69f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -92,32 +55,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprgraphics": {
|
||||
"inputs": {
|
||||
"hyprutils": [
|
||||
"hyprutils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755678602,
|
||||
"narHash": "sha256-uEC5O/NIUNs1zmc1aH1+G3GRACbODjk2iS0ET5hXtuk=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "157cc52065a104fc3b8fa542ae648b992421d1c7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-protocols": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -128,11 +65,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749046714,
|
||||
"narHash": "sha256-kymV5FMnddYGI+UjwIw8ceDjdeg7ToDVjbHCvUlhn14=",
|
||||
"lastModified": 1727451107,
|
||||
"narHash": "sha256-qV9savtHwmZUa0eJE294WYJjKPGB2+bJhwByFShsVyo=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "613878cb6f459c5e323aaafe1e6f388ac8a36330",
|
||||
"rev": "6b3261ee13a6d2b99de79a31d352f6996e35bde3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -141,64 +78,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hyprland-qt-support": {
|
||||
"hyprland-protocols_2": {
|
||||
"inputs": {
|
||||
"hyprlang": [
|
||||
"hyprland-qtutils",
|
||||
"hyprlang"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"hyprland-qtutils",
|
||||
"xdph",
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": [
|
||||
"hyprland-qtutils",
|
||||
"xdph",
|
||||
"systems"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749154592,
|
||||
"narHash": "sha256-DO7z5CeT/ddSGDEnK9mAXm1qlGL47L3VAHLlLXoCjhE=",
|
||||
"lastModified": 1721326555,
|
||||
"narHash": "sha256-zCu4R0CSHEactW9JqYki26gy8h9f6rHmSwj4XJmlHgg=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprland-qt-support",
|
||||
"rev": "4c8053c3c888138a30c3a6c45c2e45f5484f2074",
|
||||
"repo": "hyprland-protocols",
|
||||
"rev": "5a11232266bf1a1f5952d5b179c3f4b2facaaa84",
|
||||
"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",
|
||||
"repo": "hyprland-protocols",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -215,11 +116,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1753622892,
|
||||
"narHash": "sha256-0K+A+gmOI8IklSg5It1nyRNv0kCNL51duwnhUO/B8JA=",
|
||||
"lastModified": 1725997860,
|
||||
"narHash": "sha256-d/rZ/fHR5l1n7PeyLw0StWMNLXVU9c4HFyfskw568so=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "23f0debd2003f17bd65f851cd3f930cff8a8c809",
|
||||
"rev": "dfeb5811dd6485490cce18d6cc1e38a055eea876",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -238,11 +139,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756117388,
|
||||
"narHash": "sha256-oRDel6pNl/T2tI+nc/USU9ZP9w08dxtl7hiZxa0C/Wc=",
|
||||
"lastModified": 1727300645,
|
||||
"narHash": "sha256-OvAtVLaSRPnbXzOwlR1fVqCXR7i+ICRX3aPMCdIiv+c=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "b2ae3204845f5f2f79b4703b441252d8ad2ecfd0",
|
||||
"rev": "3f5293432b6dc6a99f26aca2eba3876d2660665c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -261,11 +162,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755184602,
|
||||
"narHash": "sha256-RCBQN8xuADB0LEgaKbfRqwm6CdyopE1xIEhNc67FAbw=",
|
||||
"lastModified": 1726874836,
|
||||
"narHash": "sha256-VKR0sf0PSNCB0wPHVKSAn41mCNVCnegWmgkrneKDhHM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprwayland-scanner",
|
||||
"rev": "b3b0f1f40ae09d4447c20608e5a4faf8bf3c492d",
|
||||
"rev": "500c81a9e1a76760371049a8d99e008ea77aa59e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -276,11 +177,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1756266583,
|
||||
"narHash": "sha256-cr748nSmpfvnhqSXPiCfUPxRz2FJnvf/RjJGvFfaCsM=",
|
||||
"lastModified": 1727348695,
|
||||
"narHash": "sha256-J+PeFKSDV+pHL7ukkfpVzCOO7mBSrrpJ3svwBFABbhI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "8a6d5427d99ec71c64f0b93d45778c889005d9c2",
|
||||
"rev": "1925c603f17fc89f4c8f6bf6f631a802ad85d784",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -290,40 +191,15 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755960406,
|
||||
"narHash": "sha256-RF7j6C1TmSTK9tYWO6CdEMtg6XZaUKcvZwOCD2SICZs=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "e891a93b193fcaf2fc8012d890dc7f0befe86ec2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"aquamarine": "aquamarine",
|
||||
"hyprcursor": "hyprcursor",
|
||||
"hyprgraphics": "hyprgraphics",
|
||||
"hyprland-protocols": "hyprland-protocols",
|
||||
"hyprland-qtutils": "hyprland-qtutils",
|
||||
"hyprlang": "hyprlang",
|
||||
"hyprutils": "hyprutils",
|
||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks",
|
||||
"systems": "systems",
|
||||
"xdph": "xdph"
|
||||
}
|
||||
@@ -345,9 +221,7 @@
|
||||
},
|
||||
"xdph": {
|
||||
"inputs": {
|
||||
"hyprland-protocols": [
|
||||
"hyprland-protocols"
|
||||
],
|
||||
"hyprland-protocols": "hyprland-protocols_2",
|
||||
"hyprlang": [
|
||||
"hyprlang"
|
||||
],
|
||||
@@ -365,11 +239,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755354946,
|
||||
"narHash": "sha256-zdov5f/GcoLQc9qYIS1dUTqtJMeDqmBmo59PAxze6e4=",
|
||||
"lastModified": 1727524473,
|
||||
"narHash": "sha256-1DGktDtSWIJpnDbVoj/qpvJSH5zg6JbOfuh6xqZMap0=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "xdg-desktop-portal-hyprland",
|
||||
"rev": "a10726d6a8d0ef1a0c645378f983b6278c42eaa0",
|
||||
"rev": "7e500e679ede40e79cf2d89b5f5fa3e34923bd26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
68
flake.nix
68
flake.nix
@@ -22,26 +22,12 @@
|
||||
inputs.hyprlang.follows = "hyprlang";
|
||||
};
|
||||
|
||||
hyprgraphics = {
|
||||
url = "github:hyprwm/hyprgraphics";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
};
|
||||
|
||||
hyprland-protocols = {
|
||||
url = "github:hyprwm/hyprland-protocols";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
};
|
||||
|
||||
hyprland-qtutils = {
|
||||
url = "github:hyprwm/hyprland-qtutils";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprlang.follows = "hyprlang";
|
||||
};
|
||||
|
||||
hyprlang = {
|
||||
url = "github:hyprwm/hyprlang";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
@@ -65,16 +51,10 @@
|
||||
url = "github:hyprwm/xdg-desktop-portal-hyprland";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.systems.follows = "systems";
|
||||
inputs.hyprland-protocols.follows = "hyprland-protocols";
|
||||
inputs.hyprlang.follows = "hyprlang";
|
||||
inputs.hyprutils.follows = "hyprutils";
|
||||
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
|
||||
};
|
||||
|
||||
pre-commit-hooks = {
|
||||
url = "github:cachix/git-hooks.nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs @ {
|
||||
@@ -96,27 +76,12 @@
|
||||
pkgsCrossFor = eachSystem (system: crossSystem:
|
||||
import nixpkgs {
|
||||
localSystem = system;
|
||||
inherit crossSystem;
|
||||
crossSystem = crossSystem;
|
||||
overlays = with self.overlays; [
|
||||
hyprland-packages
|
||||
hyprland-extras
|
||||
];
|
||||
});
|
||||
pkgsDebugFor = eachSystem (system:
|
||||
import nixpkgs {
|
||||
localSystem = system;
|
||||
overlays = with self.overlays; [
|
||||
hyprland-debug
|
||||
];
|
||||
});
|
||||
pkgsDebugCrossFor = eachSystem (system: crossSystem:
|
||||
import nixpkgs {
|
||||
localSystem = system;
|
||||
inherit crossSystem;
|
||||
overlays = with self.overlays; [
|
||||
hyprland-debug
|
||||
];
|
||||
});
|
||||
in {
|
||||
overlays = import ./nix/overlays.nix {inherit self lib inputs;};
|
||||
|
||||
@@ -126,35 +91,23 @@
|
||||
self.packages.${system})
|
||||
// {
|
||||
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
|
||||
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
|
||||
src = ./.;
|
||||
hooks = {
|
||||
hyprland-treewide-formatter = {
|
||||
enable = true;
|
||||
entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
|
||||
pass_filenames = false;
|
||||
excludes = ["subprojects"];
|
||||
always_run = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
// (import ./nix/tests inputs pkgsFor.${system}));
|
||||
});
|
||||
|
||||
packages = eachSystem (system: {
|
||||
default = self.packages.${system}.hyprland;
|
||||
inherit
|
||||
(pkgsFor.${system})
|
||||
# hyprland-packages
|
||||
|
||||
hyprland
|
||||
hyprland-debug
|
||||
hyprland-legacy-renderer
|
||||
hyprland-unwrapped
|
||||
hyprtester
|
||||
# hyprland-extras
|
||||
|
||||
xdg-desktop-portal-hyprland
|
||||
;
|
||||
inherit (pkgsDebugFor.${system}) hyprland-debug;
|
||||
hyprland-cross = (pkgsCrossFor.${system} "aarch64-linux").hyprland;
|
||||
hyprland-debug-cross = (pkgsDebugCrossFor.${system} "aarch64-linux").hyprland-debug;
|
||||
});
|
||||
|
||||
devShells = eachSystem (system: {
|
||||
@@ -166,19 +119,12 @@
|
||||
hardeningDisable = ["fortify"];
|
||||
inputsFrom = [pkgsFor.${system}.hyprland];
|
||||
packages = [pkgsFor.${system}.clang-tools];
|
||||
inherit (self.checks.${system}.pre-commit-check) shellHook;
|
||||
};
|
||||
});
|
||||
|
||||
formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {});
|
||||
formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra);
|
||||
|
||||
nixosModules.default = import ./nix/module.nix inputs;
|
||||
homeManagerModules.default = import ./nix/hm-module.nix self;
|
||||
|
||||
# Hydra build jobs
|
||||
# Recent versions of Hydra can aggregate jobsets from 'hydraJobs' instead of a release.nix
|
||||
# or similar. Remember to filter large or incompatible attributes here. More eval jobs can
|
||||
# be added by merging, e.g., self.packages // self.devShells.
|
||||
hydraJobs = self.packages;
|
||||
};
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ project(
|
||||
DESCRIPTION "Control utility for Hyprland"
|
||||
)
|
||||
|
||||
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 re2)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET hyprutils>=0.1.1)
|
||||
|
||||
add_executable(hyprctl "main.cpp")
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
const std::string_view USAGE = R"#(usage: hyprctl [flags] <command> [args...|--help]
|
||||
|
||||
commands:
|
||||
@@ -22,7 +20,6 @@ commands:
|
||||
getoption <option> → Gets the config option status (values)
|
||||
globalshortcuts → Lists all global shortcuts
|
||||
hyprpaper ... → Issue a hyprpaper request
|
||||
hyprsunset ... → Issue a hyprsunset request
|
||||
instances → Lists all running instances of Hyprland with
|
||||
their info
|
||||
keyword <name> <value> → Issue a keyword to call a config keyword
|
||||
@@ -49,7 +46,6 @@ commands:
|
||||
the same format as in colors in config. Will reset
|
||||
when Hyprland's config is reloaded
|
||||
setprop ... → Sets a window property
|
||||
getprop ... → Gets a window property
|
||||
splash → Get the current splash
|
||||
switchxkblayout ... → Sets the xkb layout index for a keyboard
|
||||
systeminfo → Get system info
|
||||
@@ -83,16 +79,6 @@ requests:
|
||||
flags:
|
||||
See 'hyprctl --help')#";
|
||||
|
||||
const std::string_view HYPRSUNSET_HELP = R"#(usage: hyprctl [flags] hyprsunset <request>
|
||||
|
||||
requests:
|
||||
temperature <temp> → Enable blue-light filter
|
||||
identity → Disable blue-light filter
|
||||
gamma <gamma> → Enable gamma filter
|
||||
|
||||
flags:
|
||||
See 'hyprctl --help')#";
|
||||
|
||||
const std::string_view NOTIFY_HELP = R"#(usage: hyprctl [flags] notify <icon> <time_ms> <color> <message...>
|
||||
|
||||
icon:
|
||||
@@ -147,7 +133,7 @@ regex:
|
||||
Regular expression by which a window will be searched
|
||||
|
||||
property:
|
||||
See https://wiki.hypr.land/Configuring/Using-hyprctl/#setprop for list
|
||||
See https://wiki.hyprland.org/Configuring/Using-hyprctl/#setprop for list
|
||||
of properties
|
||||
|
||||
value:
|
||||
@@ -160,18 +146,6 @@ lock:
|
||||
flags:
|
||||
See 'hyprctl --help')#";
|
||||
|
||||
const std::string_view GETPROP_HELP = R"#(usage: hyprctl [flags] getprop <regex> <property>
|
||||
|
||||
regex:
|
||||
Regular expression by which a window will be searched
|
||||
|
||||
property:
|
||||
See https://wiki.hypr.land/Configuring/Using-hyprctl/#setprop for list
|
||||
of properties
|
||||
|
||||
flags:
|
||||
See 'hyprctl --help')#";
|
||||
|
||||
const std::string_view SWITCHXKBLAYOUT_HELP = R"#(usage: [flags] switchxkblayout <device> <cmd>
|
||||
|
||||
device:
|
||||
|
@@ -23,7 +23,7 @@ _hyprctl () {
|
||||
local words cword
|
||||
_get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword
|
||||
|
||||
declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize sendkeystate)
|
||||
declare -a literals=(resizeactive 2 changegroupactive -r moveintogroup forceallowsinput 4 ::= systeminfo all layouts setprop animationstyle switchxkblayout create denywindowfromgroup headless activebordercolor exec setcursor wayland focusurgentorlast workspacerules movecurrentworkspacetomonitor movetoworkspacesilent hyprpaper alpha inactivebordercolor movegroupwindow movecursortocorner movewindowpixel prev movewindow globalshortcuts clients dimaround setignoregrouplock splash execr monitors 0 forcenoborder -q animations 1 nomaxsize splitratio moveactive pass swapnext devices layers rounding lockactivegroup 5 moveworkspacetomonitor -f -i --quiet forcenodim pin 0 1 forceopaque forcenoshadow setfloating minsize alphaoverride sendshortcut workspaces cyclenext alterzorder togglegroup lockgroups bordersize dpms focuscurrentorlast -1 --batch notify remove instances 1 3 moveoutofgroup killactive 2 movetoworkspace movecursor configerrors closewindow swapwindow tagwindow forcerendererreload centerwindow auto focuswindow seterror nofocus alphafullscreen binds version -h togglespecialworkspace fullscreen windowdancecompat 0 keyword toggleopaque 3 --instance togglefloating renameworkspace alphafullscreenoverride activeworkspace x11 kill forceopaqueoverriden output global dispatch reload forcenoblur -j event --help disable -1 activewindow keepaspectratio dismissnotify focusmonitor movefocus plugin exit workspace fullscreenstate getoption alphainactiveoverride alphainactive decorations settiled config-only descriptions resizewindowpixel fakefullscreen rollinglog swapactiveworkspaces submap next movewindoworgroup cursorpos forcenoanims focusworkspaceoncurrentmonitor maxsize)
|
||||
declare -A literal_transitions
|
||||
literal_transitions[0]="([120]=14 [43]=2 [125]=21 [81]=2 [3]=21 [51]=2 [50]=2 [128]=2 [89]=2 [58]=21 [8]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [102]=21 [133]=7 [100]=2 [137]=2 [22]=2 [19]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [78]=21 [114]=2 [37]=2 [151]=2 [116]=2 [121]=13 [123]=21 [39]=11 [42]=21 [79]=15 [118]=12)"
|
||||
literal_transitions[1]="([81]=2 [51]=2 [50]=2 [128]=2 [8]=2 [89]=2 [10]=2 [11]=3 [130]=4 [13]=5 [97]=6 [101]=2 [133]=7 [100]=2 [22]=2 [19]=2 [137]=2 [140]=8 [25]=2 [143]=2 [107]=9 [146]=10 [69]=2 [33]=2 [34]=2 [114]=2 [37]=2 [151]=2 [116]=2 [39]=11 [118]=12 [121]=13 [120]=14 [79]=15 [43]=2)"
|
||||
|
@@ -29,7 +29,7 @@ function _hyprctl
|
||||
set COMP_CWORD (count $COMP_WORDS)
|
||||
end
|
||||
|
||||
set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate"
|
||||
set literals "resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize"
|
||||
|
||||
set descriptions
|
||||
set descriptions[1] "Resize the active window"
|
||||
@@ -48,7 +48,7 @@ function _hyprctl
|
||||
set descriptions[22] "Focus the urgent window or the last window"
|
||||
set descriptions[23] "Get the list of defined workspace rules"
|
||||
set descriptions[24] "Move the active workspace to a monitor"
|
||||
set descriptions[25] "Move window doesn't switch to the workspace"
|
||||
set descriptions[25] "Move window doesnt switch to the workspace"
|
||||
set descriptions[26] "Interact with hyprpaper if present"
|
||||
set descriptions[29] "Swap the active window with the next or previous in a group"
|
||||
set descriptions[30] "Move the cursor to the corner of the active window"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This is a file fed to complgen to generate bash/fish/zsh completions
|
||||
# This is a file feeded to complgen to generate bash/fish/zsh completions
|
||||
# Repo: https://github.com/adaszko/complgen
|
||||
# Generate completion scripts: "complgen aot --bash-script hyprctl.bash --fish-script hyprctl.fish --zsh-script hyprctl.zsh ./hyprctl.usage"
|
||||
|
||||
@@ -106,12 +106,11 @@ hyprctl [<OPTIONS>]... <ARGUMENTS>
|
||||
| (execr) "Execute a raw shell command"
|
||||
| (pass) "Pass the key to a specified window"
|
||||
| (sendshortcut) "On shortcut X sends shortcut Y to a specified window"
|
||||
| (sendkeystate) "Send a key with specific state (down/repeat/up) to a specified window (window must keep focus for events to continue)"
|
||||
| (killactive) "Close the active window"
|
||||
| (closewindow) "Close a specified window"
|
||||
| (workspace) "Change the workspace"
|
||||
| (movetoworkspace) "Move the focused window to a workspace"
|
||||
| (movetoworkspacesilent) "Move window doesn't switch to the workspace"
|
||||
| (movetoworkspacesilent) "Move window doesnt switch to the workspace"
|
||||
| (togglefloating) "Toggle the current window's floating state"
|
||||
| (setfloating) "Set the current window's floating state to true"
|
||||
| (settiled) "Set the current window's floating state to false"
|
||||
|
@@ -17,7 +17,7 @@ _hyprctl_cmd_0 () {
|
||||
}
|
||||
|
||||
_hyprctl () {
|
||||
local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize" "sendkeystate")
|
||||
local -a literals=("resizeactive" "2" "changegroupactive" "-r" "moveintogroup" "forceallowsinput" "4" "::=" "systeminfo" "all" "layouts" "setprop" "animationstyle" "switchxkblayout" "create" "denywindowfromgroup" "headless" "activebordercolor" "exec" "setcursor" "wayland" "focusurgentorlast" "workspacerules" "movecurrentworkspacetomonitor" "movetoworkspacesilent" "hyprpaper" "alpha" "inactivebordercolor" "movegroupwindow" "movecursortocorner" "movewindowpixel" "prev" "movewindow" "globalshortcuts" "clients" "dimaround" "setignoregrouplock" "splash" "execr" "monitors" "0" "forcenoborder" "-q" "animations" "1" "nomaxsize" "splitratio" "moveactive" "pass" "swapnext" "devices" "layers" "rounding" "lockactivegroup" "5" "moveworkspacetomonitor" "-f" "-i" "--quiet" "forcenodim" "pin" "0" "1" "forceopaque" "forcenoshadow" "setfloating" "minsize" "alphaoverride" "sendshortcut" "workspaces" "cyclenext" "alterzorder" "togglegroup" "lockgroups" "bordersize" "dpms" "focuscurrentorlast" "-1" "--batch" "notify" "remove" "instances" "1" "3" "moveoutofgroup" "killactive" "2" "movetoworkspace" "movecursor" "configerrors" "closewindow" "swapwindow" "tagwindow" "forcerendererreload" "centerwindow" "auto" "focuswindow" "seterror" "nofocus" "alphafullscreen" "binds" "version" "-h" "togglespecialworkspace" "fullscreen" "windowdancecompat" "0" "keyword" "toggleopaque" "3" "--instance" "togglefloating" "renameworkspace" "alphafullscreenoverride" "activeworkspace" "x11" "kill" "forceopaqueoverriden" "output" "global" "dispatch" "reload" "forcenoblur" "-j" "event" "--help" "disable" "-1" "activewindow" "keepaspectratio" "dismissnotify" "focusmonitor" "movefocus" "plugin" "exit" "workspace" "fullscreenstate" "getoption" "alphainactiveoverride" "alphainactive" "decorations" "settiled" "config-only" "descriptions" "resizewindowpixel" "fakefullscreen" "rollinglog" "swapactiveworkspaces" "submap" "next" "movewindoworgroup" "cursorpos" "forcenoanims" "focusworkspaceoncurrentmonitor" "maxsize")
|
||||
|
||||
local -A descriptions
|
||||
descriptions[1]="Resize the active window"
|
||||
@@ -36,7 +36,7 @@ _hyprctl () {
|
||||
descriptions[22]="Focus the urgent window or the last window"
|
||||
descriptions[23]="Get the list of defined workspace rules"
|
||||
descriptions[24]="Move the active workspace to a monitor"
|
||||
descriptions[25]="Move window doesn't switch to the workspace"
|
||||
descriptions[25]="Move window doesnt switch to the workspace"
|
||||
descriptions[26]="Interact with hyprpaper if present"
|
||||
descriptions[29]="Swap the active window with the next or previous in a group"
|
||||
descriptions[30]="Move the cursor to the corner of the active window"
|
||||
|
259
hyprctl/main.cpp
259
hyprctl/main.cpp
@@ -1,5 +1,3 @@
|
||||
#include <re2/re2.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -12,26 +10,29 @@
|
||||
#include <sys/un.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <ranges>
|
||||
#include <optional>
|
||||
#include <charconv>
|
||||
#include <format>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <print>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <cstdarg>
|
||||
#include <regex>
|
||||
#include <sys/socket.h>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
#include <cstring>
|
||||
using namespace Hyprutils::String;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#include "Strings.hpp"
|
||||
|
||||
#define PAD
|
||||
|
||||
std::string instanceSignature;
|
||||
bool quiet = false;
|
||||
|
||||
@@ -40,100 +41,63 @@ struct SInstanceData {
|
||||
uint64_t time;
|
||||
uint64_t pid;
|
||||
std::string wlSocket;
|
||||
bool valid = true;
|
||||
};
|
||||
|
||||
void log(const std::string_view str) {
|
||||
void log(std::string str) {
|
||||
if (quiet)
|
||||
return;
|
||||
|
||||
std::println("{}", str);
|
||||
}
|
||||
|
||||
static int getUID() {
|
||||
const auto UID = getuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
std::cout << str << "\n";
|
||||
}
|
||||
|
||||
std::string getRuntimeDir() {
|
||||
const auto XDG = getenv("XDG_RUNTIME_DIR");
|
||||
|
||||
if (!XDG) {
|
||||
const std::string USERID = std::to_string(getUID());
|
||||
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
|
||||
return "/run/user/" + USERID + "/hypr";
|
||||
}
|
||||
|
||||
return std::string{XDG} + "/hypr";
|
||||
}
|
||||
|
||||
static std::optional<uint64_t> toUInt64(const std::string_view str) {
|
||||
uint64_t value = 0;
|
||||
const auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), value);
|
||||
if (ec != std::errc() || ptr != str.data() + str.size())
|
||||
return std::nullopt;
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::optional<SInstanceData> parseInstance(const std::filesystem::directory_entry& entry) {
|
||||
if (!entry.is_directory())
|
||||
return std::nullopt;
|
||||
|
||||
const auto lockPath = entry.path() / "hyprland.lock";
|
||||
std::ifstream ifs(lockPath);
|
||||
if (!ifs.is_open())
|
||||
return std::nullopt;
|
||||
|
||||
SInstanceData data;
|
||||
data.id = entry.path().filename().string();
|
||||
|
||||
const auto first = std::string_view{data.id}.find_first_of('_');
|
||||
const auto last = std::string_view{data.id}.find_last_of('_');
|
||||
if (first == std::string_view::npos || last == std::string_view::npos || last <= first)
|
||||
return std::nullopt;
|
||||
|
||||
auto time = toUInt64(std::string_view{data.id}.substr(first + 1, last - first - 1));
|
||||
if (!time)
|
||||
return std::nullopt;
|
||||
data.time = *time;
|
||||
|
||||
std::string line;
|
||||
if (!std::getline(ifs, line))
|
||||
return std::nullopt;
|
||||
|
||||
auto pid = toUInt64(std::string_view{line});
|
||||
if (!pid)
|
||||
return std::nullopt;
|
||||
data.pid = *pid;
|
||||
|
||||
if (!std::getline(ifs, data.wlSocket))
|
||||
return std::nullopt;
|
||||
|
||||
if (std::getline(ifs, line) && !line.empty())
|
||||
return std::nullopt; // more lines than expected
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<SInstanceData> instances() {
|
||||
std::vector<SInstanceData> result;
|
||||
|
||||
std::error_code ec;
|
||||
const auto runtimeDir = getRuntimeDir();
|
||||
if (!std::filesystem::exists(runtimeDir, ec) || ec)
|
||||
return result;
|
||||
for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) {
|
||||
if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock"))
|
||||
continue;
|
||||
|
||||
std::filesystem::directory_iterator it(runtimeDir, std::filesystem::directory_options::skip_permission_denied, ec);
|
||||
if (ec)
|
||||
return result;
|
||||
// read lock
|
||||
SInstanceData* data = &result.emplace_back();
|
||||
data->id = el.path().filename().string();
|
||||
|
||||
for (const auto& el : it) {
|
||||
if (auto instance = parseInstance(el))
|
||||
result.emplace_back(std::move(*instance));
|
||||
try {
|
||||
data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1, data->id.find_last_of('_') - (data->id.find_first_of('_') + 1)));
|
||||
} catch (std::exception& e) { continue; }
|
||||
|
||||
// read file
|
||||
std::ifstream ifs(el.path().string() + "/hyprland.lock");
|
||||
|
||||
int i = 0;
|
||||
for (std::string line; std::getline(ifs, line); ++i) {
|
||||
if (i == 0) {
|
||||
try {
|
||||
data->pid = std::stoull(line);
|
||||
} catch (std::exception& e) { continue; }
|
||||
} else if (i == 1) {
|
||||
data->wlSocket = line;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
std::erase_if(result, [](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; });
|
||||
std::erase_if(result, [&](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; });
|
||||
|
||||
std::ranges::sort(result, {}, &SInstanceData::time);
|
||||
std::sort(result.begin(), result.end(), [&](const auto& a, const auto& b) { return a.time < b.time; });
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -141,7 +105,7 @@ std::vector<SInstanceData> instances() {
|
||||
static volatile bool sigintReceived = false;
|
||||
void intHandler(int sig) {
|
||||
sigintReceived = true;
|
||||
std::println("[hyprctl] SIGINT received, closing connection");
|
||||
std::cout << "[hyprctl] SIGINT received, closing connection" << std::endl;
|
||||
}
|
||||
|
||||
int rollingRead(const int socket) {
|
||||
@@ -151,12 +115,12 @@ int rollingRead(const int socket) {
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
std::array<char, BUFFER_SIZE> buffer = {0};
|
||||
long sizeWritten = 0;
|
||||
std::println("[hyprctl] reading from socket following up log:");
|
||||
std::cout << "[hyprctl] reading from socket following up log:" << std::endl;
|
||||
while (!sigintReceived) {
|
||||
sizeWritten = read(socket, buffer.data(), BUFFER_SIZE);
|
||||
if (sizeWritten < 0 && errno != EAGAIN) {
|
||||
if (errno != EINTR)
|
||||
std::println("Couldn't read (5): {}: {}", strerror(errno), errno);
|
||||
std::cout << "Couldn't read (5) " << strerror(errno) << ":" << errno << std::endl;
|
||||
close(socket);
|
||||
return 5;
|
||||
}
|
||||
@@ -165,7 +129,7 @@ int rollingRead(const int socket) {
|
||||
break;
|
||||
|
||||
if (sizeWritten > 0) {
|
||||
std::println("{}", std::string(buffer.data(), sizeWritten));
|
||||
std::cout << std::string(buffer.data(), sizeWritten);
|
||||
buffer.fill('\0');
|
||||
}
|
||||
|
||||
@@ -175,19 +139,11 @@ int rollingRead(const int socket) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
|
||||
int request(std::string arg, int minArgs = 0, bool needRoll = false) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
log("Couldn't open a socket (1)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
if (setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval)) < 0) {
|
||||
log("Couldn't set socket timeout (2)");
|
||||
return 2;
|
||||
}
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
|
||||
|
||||
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
|
||||
|
||||
@@ -196,53 +152,59 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (instanceSignature.empty()) {
|
||||
log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?) (3)");
|
||||
return 3;
|
||||
if (SERVERSOCKET < 0) {
|
||||
log("Couldn't open a socket (1)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
if (instanceSignature.empty()) {
|
||||
log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.socket.sock";
|
||||
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(SERVERSOCKET, rc<sockaddr*>(&serverAddress), SUN_LEN(&serverAddress)) < 0) {
|
||||
log("Couldn't connect to " + socketPath + ". (4)");
|
||||
return 4;
|
||||
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
|
||||
log("Couldn't connect to " + socketPath + ". (3)");
|
||||
return 3;
|
||||
}
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, arg.data(), arg.size());
|
||||
auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
log("Couldn't write (5)");
|
||||
return 5;
|
||||
log("Couldn't write (4)");
|
||||
return 4;
|
||||
}
|
||||
|
||||
if (needRoll)
|
||||
return rollingRead(SERVERSOCKET);
|
||||
|
||||
std::string reply = "";
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
std::string reply = "";
|
||||
char buffer[8192] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
log("Hyprland IPC didn't respond in time\n");
|
||||
log("Couldn't read (6)");
|
||||
return 6;
|
||||
log("Couldn't read (5)");
|
||||
return 5;
|
||||
}
|
||||
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
|
||||
while (sizeWritten == BUFFER_SIZE) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
while (sizeWritten == 8192) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
if (sizeWritten < 0) {
|
||||
log("Couldn't read (6)");
|
||||
return 6;
|
||||
log("Couldn't read (5)");
|
||||
return 5;
|
||||
}
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
}
|
||||
@@ -254,7 +216,7 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int requestIPC(std::string_view filename, std::string_view arg) {
|
||||
int requestHyprpaper(std::string arg) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
@@ -270,11 +232,13 @@ int requestIPC(std::string_view filename, std::string_view arg) {
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/" + filename;
|
||||
const std::string USERID = std::to_string(getpwuid(getuid())->pw_uid);
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + instanceSignature + "/.hyprpaper.sock";
|
||||
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(SERVERSOCKET, rc<sockaddr*>(&serverAddress), SUN_LEN(&serverAddress)) < 0) {
|
||||
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
|
||||
log("Couldn't connect to " + socketPath + ". (3)");
|
||||
return 3;
|
||||
}
|
||||
@@ -282,16 +246,16 @@ int requestIPC(std::string_view filename, std::string_view arg) {
|
||||
arg = arg.substr(arg.find_first_of('/') + 1); // strip flags
|
||||
arg = arg.substr(arg.find_first_of(' ') + 1); // strip "hyprpaper"
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, arg.data(), arg.size());
|
||||
auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
log("Couldn't write (4)");
|
||||
return 4;
|
||||
}
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
char buffer[8192] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
log("Couldn't read (5)");
|
||||
@@ -305,20 +269,11 @@ 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);
|
||||
}
|
||||
|
||||
void batchRequest(std::string_view arg, bool json) {
|
||||
std::string commands(arg.substr(arg.find_first_of(' ') + 1));
|
||||
void batchRequest(std::string arg, bool json) {
|
||||
std::string commands = arg.substr(arg.find_first_of(" ") + 1);
|
||||
|
||||
if (json) {
|
||||
RE2::GlobalReplace(&commands, ";\\s*", ";j/");
|
||||
commands.insert(0, "j/");
|
||||
commands = "j/" + std::regex_replace(commands, std::regex(";\\s*"), ";j/");
|
||||
}
|
||||
|
||||
std::string rq = "[[BATCH]]" + commands;
|
||||
@@ -355,11 +310,11 @@ void instancesRequest(bool json) {
|
||||
log(result + "\n");
|
||||
}
|
||||
|
||||
std::vector<std::string> splitArgs(int argc, char** argv) {
|
||||
std::vector<std::string> result;
|
||||
std::deque<std::string> splitArgs(int argc, char** argv) {
|
||||
std::deque<std::string> result;
|
||||
|
||||
for (auto i = 1 /* skip the executable */; i < argc; ++i)
|
||||
result.emplace_back(argv[i]);
|
||||
result.push_back(std::string(argv[i]));
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -368,7 +323,7 @@ int main(int argc, char** argv) {
|
||||
bool parseArgs = true;
|
||||
|
||||
if (argc < 2) {
|
||||
std::println("{}", USAGE);
|
||||
std::cout << USAGE << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -385,7 +340,7 @@ int main(int argc, char** argv) {
|
||||
parseArgs = false;
|
||||
continue;
|
||||
}
|
||||
if (parseArgs && (ARGS[i][0] == '-') && !(isNumber(ARGS[i], true) || isNumber(ARGS[i].substr(0, ARGS[i].length() - 1), true)) /* For stuff like -2 or -2, */) {
|
||||
if (parseArgs && (ARGS[i][0] == '-') && !isNumber(ARGS[i], true) /* For stuff like -2 */) {
|
||||
// parse
|
||||
if (ARGS[i] == "-j" && !fullArgs.contains("j")) {
|
||||
fullArgs += "j";
|
||||
@@ -405,7 +360,7 @@ int main(int argc, char** argv) {
|
||||
++i;
|
||||
|
||||
if (i >= ARGS.size()) {
|
||||
std::println("{}", USAGE);
|
||||
std::cout << USAGE << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -416,28 +371,24 @@ int main(int argc, char** argv) {
|
||||
const std::string& cmd = ARGS[0];
|
||||
|
||||
if (cmd == "hyprpaper") {
|
||||
std::println("{}", HYPRPAPER_HELP);
|
||||
} else if (cmd == "hyprsunset") {
|
||||
std::println("{}", HYPRSUNSET_HELP);
|
||||
std::cout << HYPRPAPER_HELP << std::endl;
|
||||
} else if (cmd == "notify") {
|
||||
std::println("{}", NOTIFY_HELP);
|
||||
std::cout << NOTIFY_HELP << std::endl;
|
||||
} else if (cmd == "output") {
|
||||
std::println("{}", OUTPUT_HELP);
|
||||
std::cout << OUTPUT_HELP << std::endl;
|
||||
} else if (cmd == "plugin") {
|
||||
std::println("{}", PLUGIN_HELP);
|
||||
std::cout << PLUGIN_HELP << std::endl;
|
||||
} else if (cmd == "setprop") {
|
||||
std::println("{}", SETPROP_HELP);
|
||||
} else if (cmd == "getprop") {
|
||||
std::println("{}", GETPROP_HELP);
|
||||
std::cout << SETPROP_HELP << std::endl;
|
||||
} else if (cmd == "switchxkblayout") {
|
||||
std::println("{}", SWITCHXKBLAYOUT_HELP);
|
||||
std::cout << SWITCHXKBLAYOUT_HELP << std::endl;
|
||||
} else {
|
||||
std::println("{}", USAGE);
|
||||
std::cout << USAGE << std::endl;
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
std::println("{}", USAGE);
|
||||
std::cout << USAGE << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -448,7 +399,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (fullRequest.empty()) {
|
||||
std::println("{}", USAGE);
|
||||
std::cout << USAGE << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -479,7 +430,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
const auto INSTANCES = instances();
|
||||
|
||||
if (INSTANCENO < 0 || sc<std::size_t>(INSTANCENO) >= INSTANCES.size()) {
|
||||
if (INSTANCENO < 0 || static_cast<std::size_t>(INSTANCENO) >= INSTANCES.size()) {
|
||||
log("no such instance\n");
|
||||
return 1;
|
||||
}
|
||||
@@ -502,8 +453,6 @@ int main(int argc, char** argv) {
|
||||
batchRequest(fullRequest, json);
|
||||
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);
|
||||
else if (fullRequest.contains("/seterror"))
|
||||
@@ -527,7 +476,7 @@ int main(int argc, char** argv) {
|
||||
else if (fullRequest.contains("/decorations"))
|
||||
exitStatus = request(fullRequest, 1);
|
||||
else if (fullRequest.contains("/--help"))
|
||||
std::println("{}", USAGE);
|
||||
std::cout << USAGE << std::endl;
|
||||
else if (fullRequest.contains("/rollinglog") && needRoll)
|
||||
exitStatus = request(fullRequest, 0, true);
|
||||
else {
|
||||
|
@@ -3,7 +3,6 @@ executable(
|
||||
'main.cpp',
|
||||
dependencies: [
|
||||
dependency('hyprutils', version: '>= 0.1.1'),
|
||||
dependency('re2', required: true)
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
|
@@ -9,25 +9,11 @@ file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
|
||||
|
||||
find_package(glaze QUIET)
|
||||
if (NOT glaze_FOUND)
|
||||
set(GLAZE_VERSION v5.1.1)
|
||||
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
glaze
|
||||
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
|
||||
GIT_TAG ${GLAZE_VERSION}
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(glaze)
|
||||
endif()
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.1.1)
|
||||
|
||||
add_executable(hyprpm ${SRCFILES})
|
||||
|
||||
target_link_libraries(hyprpm PUBLIC PkgConfig::hyprpm_deps glaze::glaze)
|
||||
target_link_libraries(hyprpm PUBLIC PkgConfig::deps)
|
||||
|
||||
# binary
|
||||
install(TARGETS hyprpm)
|
||||
|
@@ -29,8 +29,8 @@ function _hyprpm
|
||||
set descriptions[6] "Show help menu"
|
||||
set descriptions[7] "Check and update all plugins if needed"
|
||||
set descriptions[8] "Install a new plugin repository from git"
|
||||
set descriptions[9] "Enable too much logging"
|
||||
set descriptions[10] "Enable too much logging"
|
||||
set descriptions[9] "Enable too much loggin"
|
||||
set descriptions[10] "Enable too much loggin"
|
||||
set descriptions[11] "Force an operation ignoring checks (e.g. update -f)"
|
||||
set descriptions[12] "Disable shallow cloning of Hyprland sources"
|
||||
set descriptions[13] "Remove a plugin repository"
|
||||
|
@@ -3,7 +3,7 @@ hyprpm [<FLAGS>]... <ARGUMENT>
|
||||
|
||||
<FLAGS> ::= (--notify | -n) "Send a hyprland notification for important events (e.g. load fail)"
|
||||
| (--help | -h) "Show help menu"
|
||||
| (--verbose | -v) "Enable too much logging"
|
||||
| (--verbose | -v) "Enable too much loggin"
|
||||
| (--force | -f) "Force an operation ignoring checks (e.g. update -f)"
|
||||
| (--no-shallow | -s) "Disable shallow cloning of Hyprland sources"
|
||||
;
|
||||
@@ -14,7 +14,7 @@ hyprpm [<FLAGS>]... <ARGUMENT>
|
||||
| (list) "List all installed plugins"
|
||||
| (enable <PLUGINS>) "Load a plugin"
|
||||
| (disable <PLUGINS>) "Unload a plugin"
|
||||
| (reload) "Reload plugins to match the enabled/disabled state. Use -f to force reload."
|
||||
| (reload) "Reload all plugins"
|
||||
;
|
||||
|
||||
<PLUGINS> ::= {{{ hyprpm list | awk '/Plugin/{print $4}' }}};
|
||||
|
@@ -19,8 +19,8 @@ _hyprpm () {
|
||||
descriptions[6]="Show help menu"
|
||||
descriptions[7]="Check and update all plugins if needed"
|
||||
descriptions[8]="Install a new plugin repository from git"
|
||||
descriptions[9]="Enable too much logging"
|
||||
descriptions[10]="Enable too much logging"
|
||||
descriptions[9]="Enable too much loggin"
|
||||
descriptions[10]="Enable too much loggin"
|
||||
descriptions[11]="Force an operation ignoring checks (e.g. update -f)"
|
||||
descriptions[12]="Disable shallow cloning of Hyprland sources"
|
||||
descriptions[13]="Remove a plugin repository"
|
||||
|
@@ -1,91 +1,45 @@
|
||||
#include "DataState.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <toml++/toml.hpp>
|
||||
#include <print>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include "PluginManager.hpp"
|
||||
#include "../helpers/Die.hpp"
|
||||
#include "../helpers/Sys.hpp"
|
||||
#include "../helpers/StringUtils.hpp"
|
||||
|
||||
static std::string getTempRoot() {
|
||||
static auto ENV = getenv("XDG_RUNTIME_DIR");
|
||||
if (!ENV) {
|
||||
std::cerr << "\nERROR: XDG_RUNTIME_DIR not set!\n";
|
||||
exit(1);
|
||||
std::string DataState::getDataStatePath() {
|
||||
const auto HOME = getenv("HOME");
|
||||
if (!HOME) {
|
||||
std::cerr << "DataState: no $HOME\n";
|
||||
throw std::runtime_error("no $HOME");
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto STR = ENV + std::string{"/hyprpm/"};
|
||||
const auto XDG_DATA_HOME = getenv("XDG_DATA_HOME");
|
||||
|
||||
return STR;
|
||||
}
|
||||
|
||||
// write the state to a file
|
||||
static bool writeState(const std::string& str, const std::string& to) {
|
||||
// create temp file in a safe temp root
|
||||
std::ofstream of(getTempRoot() + ".temp-state", std::ios::trunc);
|
||||
if (!of.good())
|
||||
return false;
|
||||
|
||||
of << str;
|
||||
of.close();
|
||||
|
||||
return NSys::root::install(getTempRoot() + ".temp-state", to, "644");
|
||||
}
|
||||
|
||||
std::filesystem::path DataState::getDataStatePath() {
|
||||
return std::filesystem::path("/var/cache/hyprpm/" + g_pPluginManager->m_szUsername);
|
||||
if (XDG_DATA_HOME)
|
||||
return std::string{XDG_DATA_HOME} + "/hyprpm";
|
||||
return std::string{HOME} + "/.local/share/hyprpm";
|
||||
}
|
||||
|
||||
std::string DataState::getHeadersPath() {
|
||||
return getDataStatePath() / "headersRoot";
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> DataState::getPluginStates() {
|
||||
ensureStateStoreExists();
|
||||
|
||||
std::vector<std::filesystem::path> states;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(getDataStatePath())) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
const auto stateFile = entry.path() / "state.toml";
|
||||
if (!std::filesystem::exists(stateFile))
|
||||
continue;
|
||||
|
||||
states.emplace_back(stateFile);
|
||||
}
|
||||
return states;
|
||||
return getDataStatePath() + "/headersRoot";
|
||||
}
|
||||
|
||||
void DataState::ensureStateStoreExists() {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(getHeadersPath(), ec) || ec) {
|
||||
std::println("{}", infoString("The hyprpm state store doesn't exist. Creating now..."));
|
||||
if (!std::filesystem::exists("/var/cache/hyprpm/", ec) || ec) {
|
||||
if (!NSys::root::createDirectory("/var/cache/hyprpm", "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
if (!std::filesystem::exists(getDataStatePath(), ec) || ec) {
|
||||
if (!NSys::root::createDirectory(getDataStatePath().string(), "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
if (!NSys::root::createDirectory(getHeadersPath(), "755"))
|
||||
Debug::die("ensureStateStoreExists: Failed to run a superuser cmd");
|
||||
}
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
if (!std::filesystem::exists(PATH))
|
||||
std::filesystem::create_directories(PATH);
|
||||
|
||||
if (!std::filesystem::exists(getHeadersPath()))
|
||||
std::filesystem::create_directories(getHeadersPath());
|
||||
}
|
||||
|
||||
void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath() / repo.name;
|
||||
const auto PATH = getDataStatePath() + "/" + repo.name;
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(PATH, ec) || ec) {
|
||||
if (!NSys::root::createDirectory(PATH.string(), "755"))
|
||||
Debug::die("addNewPluginRepo: failed to create cache dir");
|
||||
}
|
||||
std::filesystem::create_directories(PATH);
|
||||
// clang-format off
|
||||
auto DATA = toml::table{
|
||||
{"repository", toml::table{
|
||||
@@ -96,36 +50,39 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||
}}
|
||||
};
|
||||
for (auto const& p : repo.plugins) {
|
||||
const auto filename = p.name + ".so";
|
||||
|
||||
// copy .so to the good place and chmod 755
|
||||
if (std::filesystem::exists(p.filename)) {
|
||||
if (!NSys::root::install(p.filename, (PATH / filename).string(), "0755"))
|
||||
Debug::die("addNewPluginRepo: failed to install so file");
|
||||
}
|
||||
// copy .so to the good place
|
||||
if (std::filesystem::exists(p.filename))
|
||||
std::filesystem::copy_file(p.filename, PATH + "/" + p.name + ".so");
|
||||
|
||||
DATA.emplace(p.name, toml::table{
|
||||
{"filename", filename},
|
||||
{"filename", p.name + ".so"},
|
||||
{"enabled", p.enabled},
|
||||
{"failed", p.failed}
|
||||
});
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
std::stringstream ss;
|
||||
ss << DATA;
|
||||
|
||||
if (!writeState(ss.str(), (PATH / "state.toml").string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
|
||||
ofs << DATA;
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
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 URL = STATE["repository"]["url"].value_or("");
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
|
||||
if (URL == urlOrName || NAME == urlOrName)
|
||||
return true;
|
||||
@@ -137,29 +94,31 @@ bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
||||
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 URL = STATE["repository"]["url"].value_or("");
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
|
||||
if (URL == urlOrName || NAME == urlOrName) {
|
||||
|
||||
// unload the plugins!!
|
||||
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
|
||||
for (const auto& file : std::filesystem::directory_iterator(entry.path())) {
|
||||
if (!file.path().string().ends_with(".so"))
|
||||
continue;
|
||||
|
||||
g_pPluginManager->loadUnloadPlugin(std::filesystem::absolute(file.path()), false);
|
||||
}
|
||||
|
||||
const auto PATH = stateFile.parent_path().string();
|
||||
|
||||
if (!PATH.starts_with("/var/cache/hyprpm") || PATH.contains('\''))
|
||||
return; // WTF?
|
||||
|
||||
// scary!
|
||||
if (!NSys::root::removeRecursive(PATH))
|
||||
Debug::die("removePluginRepo: failed to remove dir");
|
||||
std::filesystem::remove_all(entry.path());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -168,13 +127,9 @@ void DataState::removePluginRepo(const std::string& urlOrName) {
|
||||
void DataState::updateGlobalState(const SGlobalState& state) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(PATH, ec) || ec) {
|
||||
if (!NSys::root::createDirectory(PATH.string(), "755"))
|
||||
Debug::die("updateGlobalState: failed to create dir");
|
||||
}
|
||||
std::filesystem::create_directories(PATH);
|
||||
// clang-format off
|
||||
auto DATA = toml::table{
|
||||
{"state", toml::table{
|
||||
@@ -184,23 +139,20 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
std::stringstream ss;
|
||||
ss << DATA;
|
||||
|
||||
if (!writeState(ss.str(), (PATH / "state.toml").string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
std::ofstream ofs(PATH + "/state.toml", std::ios::trunc);
|
||||
ofs << DATA;
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
SGlobalState DataState::getGlobalState() {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto stateFile = getDataStatePath() / "state.toml";
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(stateFile, ec) || ec)
|
||||
if (!std::filesystem::exists(PATH + "/state.toml"))
|
||||
return SGlobalState{};
|
||||
|
||||
auto DATA = toml::parse_file(stateFile.c_str());
|
||||
auto DATA = toml::parse_file(PATH + "/state.toml");
|
||||
|
||||
SGlobalState state;
|
||||
state.headersHashCompiled = DATA["state"]["hash"].value_or("");
|
||||
@@ -212,9 +164,18 @@ SGlobalState DataState::getGlobalState() {
|
||||
std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||
ensureStateStoreExists();
|
||||
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
std::vector<SPluginRepository> repos;
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
|
||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||
const auto URL = STATE["repository"]["url"].value_or("");
|
||||
@@ -247,8 +208,17 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||
bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
ensureStateStoreExists();
|
||||
|
||||
for (const auto& stateFile : getPluginStates()) {
|
||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||
const auto PATH = getDataStatePath();
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(PATH)) {
|
||||
if (!entry.is_directory() || entry.path().stem() == "headersRoot")
|
||||
continue;
|
||||
|
||||
if (!std::filesystem::exists(entry.path().string() + "/state.toml"))
|
||||
continue;
|
||||
|
||||
auto STATE = toml::parse_file(entry.path().string() + "/state.toml");
|
||||
|
||||
for (const auto& [key, val] : STATE) {
|
||||
if (key == "repository")
|
||||
continue;
|
||||
@@ -261,33 +231,15 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||
if (FAILED)
|
||||
return false;
|
||||
|
||||
auto modifiedState = STATE;
|
||||
(*modifiedState[key].as_table()).insert_or_assign("enabled", enabled);
|
||||
(*STATE[key].as_table()).insert_or_assign("enabled", enabled);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << modifiedState;
|
||||
|
||||
if (!writeState(ss.str(), stateFile.string()))
|
||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||
std::ofstream state(entry.path().string() + "/state.toml", std::ios::trunc);
|
||||
state << STATE;
|
||||
state.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DataState::purgeAllCache() {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(getDataStatePath()) && !ec) {
|
||||
std::println("{}", infoString("Nothing to do"));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PATH = getDataStatePath().string();
|
||||
if (PATH.contains('\''))
|
||||
return;
|
||||
// scary!
|
||||
if (!NSys::root::removeRecursive(PATH))
|
||||
Debug::die("Failed to run a superuser cmd");
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Plugin.hpp"
|
||||
@@ -10,16 +9,14 @@ struct SGlobalState {
|
||||
};
|
||||
|
||||
namespace DataState {
|
||||
std::filesystem::path getDataStatePath();
|
||||
std::string getHeadersPath();
|
||||
std::vector<std::filesystem::path> getPluginStates();
|
||||
void ensureStateStoreExists();
|
||||
void addNewPluginRepo(const SPluginRepository& repo);
|
||||
void removePluginRepo(const std::string& urlOrName);
|
||||
bool pluginRepoExists(const std::string& urlOrName);
|
||||
void updateGlobalState(const SGlobalState& state);
|
||||
void purgeAllCache();
|
||||
SGlobalState getGlobalState();
|
||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
||||
std::vector<SPluginRepository> getAllRepositories();
|
||||
std::string getDataStatePath();
|
||||
std::string getHeadersPath();
|
||||
void ensureStateStoreExists();
|
||||
void addNewPluginRepo(const SPluginRepository& repo);
|
||||
void removePluginRepo(const std::string& urlOrName);
|
||||
bool pluginRepoExists(const std::string& urlOrName);
|
||||
void updateGlobalState(const SGlobalState& state);
|
||||
SGlobalState getGlobalState();
|
||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
||||
std::vector<SPluginRepository> getAllRepositories();
|
||||
};
|
@@ -1,89 +0,0 @@
|
||||
#include "HyprlandSocket.hpp"
|
||||
#include <pwd.h>
|
||||
#include <sys/socket.h>
|
||||
#include "../helpers/StringUtils.hpp"
|
||||
#include <print>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static int getUID() {
|
||||
const auto UID = getuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
static std::string getRuntimeDir() {
|
||||
const auto XDG = getenv("XDG_RUNTIME_DIR");
|
||||
|
||||
if (!XDG) {
|
||||
const std::string USERID = std::to_string(getUID());
|
||||
return "/run/user/" + USERID + "/hypr";
|
||||
}
|
||||
|
||||
return std::string{XDG} + "/hypr";
|
||||
}
|
||||
|
||||
std::string NHyprlandSocket::send(const std::string& cmd) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
std::println("{}", failureString("Couldn't open a socket (1)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (!HIS) {
|
||||
std::println("{}", failureString("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?) (3)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + HIS + "/.socket.sock";
|
||||
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(SERVERSOCKET, rc<sockaddr*>(&serverAddress), SUN_LEN(&serverAddress)) < 0) {
|
||||
std::println("{}", failureString("Couldn't connect to " + socketPath + ". (4)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, cmd.c_str(), cmd.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
std::println("{}", failureString("Couldn't write (5)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string reply = "";
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
char buffer[BUFFER_SIZE] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
std::println("{}", failureString("Couldn't read (6)"));
|
||||
return "";
|
||||
}
|
||||
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
|
||||
while (sizeWritten == BUFFER_SIZE) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||
if (sizeWritten < 0) {
|
||||
std::println("{}", failureString("Couldn't read (7)"));
|
||||
return "";
|
||||
}
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
}
|
||||
|
||||
close(SERVERSOCKET);
|
||||
|
||||
return reply;
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace NHyprlandSocket {
|
||||
std::string send(const std::string& cmd);
|
||||
};
|
@@ -1,12 +1,6 @@
|
||||
#include "Manifest.hpp"
|
||||
#include <toml++/toml.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
// Alphanumerics and -_ allowed for plugin names. No magic names.
|
||||
// [A-Za-z0-9\-_]*
|
||||
static bool validManifestName(const std::string_view& n) {
|
||||
return std::ranges::all_of(n, [](const char& c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '_' || c == '=' || (c >= '0' && c <= '9'); });
|
||||
}
|
||||
#include <iostream>
|
||||
|
||||
CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
auto manifest = toml::parse_file(path);
|
||||
@@ -17,17 +11,11 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
continue;
|
||||
|
||||
CManifest::SManifestPlugin plugin;
|
||||
|
||||
if (!validManifestName(key.str())) {
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.name = key;
|
||||
m_plugins.push_back(plugin);
|
||||
m_vPlugins.push_back(plugin);
|
||||
}
|
||||
|
||||
for (auto& plugin : m_plugins) {
|
||||
for (auto& plugin : m_vPlugins) {
|
||||
plugin.description = manifest[plugin.name]["description"].value_or("?");
|
||||
plugin.version = manifest[plugin.name]["version"].value_or("?");
|
||||
plugin.output = manifest[plugin.name]["build"]["output"].value_or("?");
|
||||
@@ -49,21 +37,21 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
}
|
||||
|
||||
if (plugin.output.empty() || plugin.buildSteps.empty()) {
|
||||
m_good = false;
|
||||
m_bGood = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (type == MANIFEST_HYPRPM) {
|
||||
m_repository.name = manifest["repository"]["name"].value_or("");
|
||||
auto authors = manifest["repository"]["authors"].as_array();
|
||||
m_sRepository.name = manifest["repository"]["name"].value_or("");
|
||||
auto authors = manifest["repository"]["authors"].as_array();
|
||||
if (authors) {
|
||||
for (auto&& a : *authors) {
|
||||
m_repository.authors.push_back(a.as_string()->value_or("?"));
|
||||
m_sRepository.authors.push_back(a.as_string()->value_or("?"));
|
||||
}
|
||||
} else {
|
||||
auto author = manifest["repository"]["author"].value_or("");
|
||||
if (!std::string{author}.empty())
|
||||
m_repository.authors.push_back(author);
|
||||
m_sRepository.authors.push_back(author);
|
||||
}
|
||||
|
||||
auto pins = manifest["repository"]["commit_pins"].as_array();
|
||||
@@ -71,7 +59,7 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
for (auto&& pin : *pins) {
|
||||
auto pinArr = pin.as_array();
|
||||
if (pinArr && pinArr->get(1))
|
||||
m_repository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
|
||||
m_sRepository.commitPins.push_back(std::make_pair<>(pinArr->get(0)->as_string()->get(), pinArr->get(1)->as_string()->get()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,17 +68,11 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
continue;
|
||||
|
||||
CManifest::SManifestPlugin plugin;
|
||||
|
||||
if (!validManifestName(key.str())) {
|
||||
m_good = false;
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.name = key;
|
||||
m_plugins.push_back(plugin);
|
||||
m_vPlugins.push_back(plugin);
|
||||
}
|
||||
|
||||
for (auto& plugin : m_plugins) {
|
||||
for (auto& plugin : m_vPlugins) {
|
||||
plugin.description = manifest[plugin.name]["description"].value_or("?");
|
||||
plugin.output = manifest[plugin.name]["output"].value_or("?");
|
||||
plugin.since = manifest[plugin.name]["since_hyprland"].value_or(0);
|
||||
@@ -112,12 +94,12 @@ CManifest::CManifest(const eManifestType type, const std::string& path) {
|
||||
}
|
||||
|
||||
if (plugin.output.empty() || plugin.buildSteps.empty()) {
|
||||
m_good = false;
|
||||
m_bGood = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ???
|
||||
m_good = false;
|
||||
m_bGood = false;
|
||||
}
|
||||
}
|
@@ -27,8 +27,8 @@ class CManifest {
|
||||
std::string name;
|
||||
std::vector<std::string> authors;
|
||||
std::vector<std::pair<std::string, std::string>> commitPins;
|
||||
} m_repository;
|
||||
} m_sRepository;
|
||||
|
||||
std::vector<SManifestPlugin> m_plugins;
|
||||
bool m_good = true;
|
||||
std::vector<SManifestPlugin> m_vPlugins;
|
||||
bool m_bGood = true;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
enum eHeadersErrors {
|
||||
HEADERS_OK = 0,
|
||||
@@ -28,8 +26,7 @@ enum ePluginLoadStateReturn {
|
||||
LOADSTATE_OK = 0,
|
||||
LOADSTATE_FAIL,
|
||||
LOADSTATE_PARTIAL_FAIL,
|
||||
LOADSTATE_HEADERS_OUTDATED,
|
||||
LOADSTATE_HYPRLAND_UPDATED
|
||||
LOADSTATE_HEADERS_OUTDATED
|
||||
};
|
||||
|
||||
struct SHyprlandVersion {
|
||||
@@ -41,8 +38,6 @@ struct SHyprlandVersion {
|
||||
|
||||
class CPluginManager {
|
||||
public:
|
||||
CPluginManager();
|
||||
|
||||
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
||||
bool removePluginRepo(const std::string& urlOrName);
|
||||
|
||||
@@ -54,10 +49,10 @@ class CPluginManager {
|
||||
|
||||
bool enablePlugin(const std::string& name);
|
||||
bool disablePlugin(const std::string& name);
|
||||
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
|
||||
ePluginLoadStateReturn ensurePluginsLoadState();
|
||||
|
||||
bool loadUnloadPlugin(const std::string& path, bool load);
|
||||
SHyprlandVersion getHyprlandVersion(bool running = true);
|
||||
SHyprlandVersion getHyprlandVersion();
|
||||
|
||||
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);
|
||||
|
||||
@@ -65,7 +60,6 @@ class CPluginManager {
|
||||
|
||||
bool m_bVerbose = false;
|
||||
bool m_bNoShallow = false;
|
||||
std::string m_szCustomHlUrl, m_szUsername;
|
||||
|
||||
// will delete recursively if exists!!
|
||||
bool createSafeDirectory(const std::string& path);
|
||||
@@ -74,7 +68,7 @@ class CPluginManager {
|
||||
std::string headerError(const eHeadersErrors err);
|
||||
std::string headerErrorShort(const eHeadersErrors err);
|
||||
|
||||
std::string m_szWorkingPluginDirectory;
|
||||
std::string m_szWorkingPluginDirectory = "";
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CPluginManager> g_pPluginManager;
|
||||
|
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
namespace Debug {
|
||||
template <typename... Args>
|
||||
void die(std::format_string<Args...> fmt, Args&&... args) {
|
||||
const std::string logMsg = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::cout << "\n[ERR] " << logMsg << "\n";
|
||||
exit(1);
|
||||
}
|
||||
};
|
@@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <string>
|
||||
#include "Colors.hpp"
|
||||
|
||||
template <typename... Args>
|
||||
std::string statusString(const std::string_view emoji, const std::string_view color, const std::string_view fmt, Args&&... args) {
|
||||
std::string ret = std::format("{}{}{} ", color, emoji, Colors::RESET);
|
||||
ret += std::vformat(fmt, std::make_format_args(args...));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string successString(const std::string_view fmt, Args&&... args) {
|
||||
return statusString("✔", Colors::GREEN, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string failureString(const std::string_view fmt, Args&&... args) {
|
||||
return statusString("✖", Colors::RED, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string verboseString(const std::string_view fmt, Args&&... args) {
|
||||
return statusString("[v]", Colors::BLUE, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string infoString(const std::string_view fmt, Args&&... args) {
|
||||
return statusString("→", Colors::RESET, fmt, args...);
|
||||
}
|
@@ -1,169 +0,0 @@
|
||||
#include "Sys.hpp"
|
||||
#include "Die.hpp"
|
||||
#include "StringUtils.hpp"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <print>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
inline constexpr std::array<std::string_view, 3> SUPERUSER_BINARIES = {
|
||||
"sudo",
|
||||
"doas",
|
||||
"run0",
|
||||
};
|
||||
|
||||
static std::string validSubinsAsStr() {
|
||||
std::ostringstream oss;
|
||||
auto it = SUPERUSER_BINARIES.begin();
|
||||
if (it != SUPERUSER_BINARIES.end()) {
|
||||
oss << *it++;
|
||||
for (; it != SUPERUSER_BINARIES.end(); ++it)
|
||||
oss << ", " << *it;
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
static bool executableExistsInPath(const std::string& exe) {
|
||||
const char* PATHENV = std::getenv("PATH");
|
||||
if (!PATHENV)
|
||||
return false;
|
||||
|
||||
CVarList paths(PATHENV, 0, ':', true);
|
||||
std::error_code ec;
|
||||
|
||||
for (const auto& PATH : paths) {
|
||||
std::filesystem::path candidate = std::filesystem::path(PATH) / exe;
|
||||
if (!std::filesystem::exists(candidate, ec) || ec)
|
||||
continue;
|
||||
if (!std::filesystem::is_regular_file(candidate, ec) || ec)
|
||||
continue;
|
||||
auto perms = std::filesystem::status(candidate, ec).permissions();
|
||||
if (ec)
|
||||
continue;
|
||||
if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string subin() {
|
||||
static std::string bin;
|
||||
static bool once = true;
|
||||
if (!once)
|
||||
return bin;
|
||||
|
||||
for (const auto& BIN : SUPERUSER_BINARIES) {
|
||||
if (!executableExistsInPath(std::string{BIN}))
|
||||
continue;
|
||||
|
||||
bin = BIN;
|
||||
break;
|
||||
}
|
||||
|
||||
once = false;
|
||||
|
||||
if (bin.empty())
|
||||
Debug::die("{}", failureString("No valid superuser binary present. Supported: {}", validSubinsAsStr()));
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
static bool verifyStringValid(const std::string& s) {
|
||||
return std::ranges::none_of(s, [](const char& c) { return c == '`' || c == '$' || c == '(' || c == ')' || c == '\'' || c == '"'; });
|
||||
}
|
||||
|
||||
int NSys::getUID() {
|
||||
const auto UID = getuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
int NSys::getEUID() {
|
||||
const auto UID = geteuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
bool NSys::isSuperuser() {
|
||||
return getuid() != geteuid() || geteuid() == 0;
|
||||
}
|
||||
|
||||
void NSys::root::cacheSudo() {
|
||||
// "caches" the sudo so that the prompt later doesn't pop up in a weird spot
|
||||
// sudo will not ask us again for a moment
|
||||
CProcess proc(subin(), {"echo", "hyprland"});
|
||||
proc.runSync();
|
||||
}
|
||||
|
||||
void NSys::root::dropSudo() {
|
||||
if (subin() != "sudo") {
|
||||
std::println("{}", infoString("Don't know how to drop timestamp for '{}', ignoring.", subin()));
|
||||
return;
|
||||
}
|
||||
|
||||
CProcess proc(subin(), {"-k"});
|
||||
proc.runSync();
|
||||
}
|
||||
|
||||
bool NSys::root::createDirectory(const std::string& path, const std::string& mode) {
|
||||
if (!verifyStringValid(path))
|
||||
return false;
|
||||
|
||||
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"mkdir", "-p", "-m", mode, path});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool NSys::root::removeRecursive(const std::string& path) {
|
||||
if (!verifyStringValid(path))
|
||||
return false;
|
||||
|
||||
std::error_code ec;
|
||||
const std::string PATH_ABSOLUTE = std::filesystem::canonical(path, ec);
|
||||
|
||||
if (ec)
|
||||
return false;
|
||||
|
||||
if (!PATH_ABSOLUTE.starts_with("/var/cache/hyprpm"))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"rm", "-fr", PATH_ABSOLUTE});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
bool NSys::root::install(const std::string& what, const std::string& where, const std::string& mode) {
|
||||
if (!verifyStringValid(what) || !verifyStringValid(where))
|
||||
return false;
|
||||
|
||||
if (!std::ranges::all_of(mode, [](const char& c) { return c >= '0' && c <= '9'; }))
|
||||
return false;
|
||||
|
||||
CProcess proc(subin(), {"install", "-m" + mode, "-o", "0", "-g", "0", what, where});
|
||||
|
||||
return proc.runSync() && proc.exitCode() == 0;
|
||||
}
|
||||
|
||||
std::string NSys::root::runAsSuperuserUnsafe(const std::string& cmd) {
|
||||
CProcess proc(subin(), {"/bin/sh", "-c", cmd});
|
||||
|
||||
if (!proc.runSync())
|
||||
return "";
|
||||
|
||||
return proc.stdOut();
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace NSys {
|
||||
bool isSuperuser();
|
||||
int getUID();
|
||||
int getEUID();
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
namespace root {
|
||||
void cacheSudo();
|
||||
void dropSudo();
|
||||
|
||||
//
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool createDirectory(const std::string& path, const std::string& mode);
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool removeRecursive(const std::string& path);
|
||||
[[nodiscard("Discarding could lead to vulnerabilities and bugs")]] bool install(const std::string& what, const std::string& where, const std::string& mode);
|
||||
|
||||
// Do not use this unless absolutely necessary!
|
||||
std::string runAsSuperuserUnsafe(const std::string& cmd);
|
||||
};
|
||||
};
|
@@ -1,139 +1,105 @@
|
||||
#include "progress/CProgressBar.hpp"
|
||||
#include "helpers/Colors.hpp"
|
||||
#include "helpers/StringUtils.hpp"
|
||||
#include "core/PluginManager.hpp"
|
||||
#include "core/DataState.hpp"
|
||||
#include "helpers/Sys.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <print>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
||||
const std::string 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] → Remove an installed plugin repository.
|
||||
┣ enable [name] → Enable a plugin.
|
||||
┣ disable [name] → Disable a plugin.
|
||||
┣ update → Check and update all plugins if needed.
|
||||
┣ 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.
|
||||
┣ list → List all installed plugins
|
||||
┃
|
||||
┣ Flags:
|
||||
┃
|
||||
┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events).
|
||||
┣ --notify-fail | -nn → Send a hyprland notification for fail events only.
|
||||
┣ --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 (e.g. load fail)
|
||||
┣ --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
|
||||
┗
|
||||
)#";
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
std::vector<std::string> ARGS{argc};
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
ARGS[i] = std::string{argv[i]};
|
||||
}
|
||||
|
||||
if (ARGS.size() < 2) {
|
||||
std::println(stderr, "{}", HELP);
|
||||
std::cout << HELP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<std::string> command;
|
||||
bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
|
||||
std::string customHlUrl;
|
||||
bool notify = false, verbose = false, force = false, noShallow = false;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (ARGS[i].starts_with("-")) {
|
||||
if (ARGS[i] == "--help" || ARGS[i] == "-h") {
|
||||
std::println("{}", HELP);
|
||||
std::cout << HELP;
|
||||
return 0;
|
||||
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
|
||||
notify = true;
|
||||
} else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") {
|
||||
notifyFail = notify = true;
|
||||
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
|
||||
verbose = true;
|
||||
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
|
||||
noShallow = true;
|
||||
} else if (ARGS[i] == "--hl-url") {
|
||||
if (i + 1 >= argc) {
|
||||
std::println(stderr, "Missing argument for --hl-url");
|
||||
return 1;
|
||||
}
|
||||
customHlUrl = ARGS[i + 1];
|
||||
i++;
|
||||
} else if (ARGS[i] == "--force" || ARGS[i] == "-f") {
|
||||
force = true;
|
||||
std::println("{}", statusString("!", Colors::RED, "Using --force, I hope you know what you are doing."));
|
||||
std::cout << Colors::RED << "!" << Colors::RESET << " Using --force, I hope you know what you are doing.\n";
|
||||
} else {
|
||||
std::println(stderr, "Unrecognized option {}", ARGS[i]);
|
||||
std::cerr << "Unrecognized option " << ARGS[i] << "\n";
|
||||
return 1;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
command.push_back(ARGS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (command.empty()) {
|
||||
std::println(stderr, "{}", HELP);
|
||||
return 1;
|
||||
std::cout << HELP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_pPluginManager = std::make_unique<CPluginManager>();
|
||||
g_pPluginManager->m_bVerbose = verbose;
|
||||
g_pPluginManager->m_bNoShallow = noShallow;
|
||||
g_pPluginManager->m_szCustomHlUrl = customHlUrl;
|
||||
g_pPluginManager = std::make_unique<CPluginManager>();
|
||||
g_pPluginManager->m_bVerbose = verbose;
|
||||
g_pPluginManager->m_bNoShallow = noShallow;
|
||||
|
||||
if (command[0] == "add") {
|
||||
if (command.size() < 2) {
|
||||
std::println(stderr, "{}", failureString("Not enough args for add."));
|
||||
std::cerr << Colors::RED << "✖" << Colors::RESET << " Not enough args for add.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string rev = "";
|
||||
if (command.size() >= 3)
|
||||
if (command.size() >= 3) {
|
||||
rev = command[2];
|
||||
|
||||
const auto HLVER = g_pPluginManager->getHyprlandVersion();
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
|
||||
if (GLOBALSTATE.headersHashCompiled != HLVER.hash) {
|
||||
std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
g_pPluginManager->updateHeaders(false);
|
||||
|
||||
return g_pPluginManager->addNewPluginRepo(command[1], rev) ? 0 : 1;
|
||||
} else if (command[0] == "remove") {
|
||||
if (command.size() < 2) {
|
||||
std::println(stderr, "{}", failureString("Not enough args for remove."));
|
||||
if (ARGS.size() < 2) {
|
||||
std::cerr << Colors::RED << "✖" << Colors::RESET << " Not enough args for remove.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
|
||||
} else if (command[0] == "update") {
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
|
||||
bool headers = g_pPluginManager->updateHeaders(force);
|
||||
|
||||
bool headersValid = g_pPluginManager->headersValid() == HEADERS_OK;
|
||||
bool headers = g_pPluginManager->updateHeaders(force);
|
||||
if (headers) {
|
||||
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
|
||||
const auto HLVER = g_pPluginManager->getHyprlandVersion();
|
||||
auto GLOBALSTATE = DataState::getGlobalState();
|
||||
const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled;
|
||||
|
||||
@@ -144,81 +110,59 @@ int main(int argc, char** argv, char** envp) {
|
||||
|
||||
auto ret2 = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
if (ret2 == LOADSTATE_HYPRLAND_UPDATED)
|
||||
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Updated plugins, but Hyprland was updated. Please restart Hyprland.");
|
||||
|
||||
if (ret2 != LOADSTATE_OK)
|
||||
return 1;
|
||||
} 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."));
|
||||
if (ARGS.size() < 2) {
|
||||
std::cerr << Colors::RED << "✖" << Colors::RESET << " Not enough args for enable.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!g_pPluginManager->enablePlugin(command[1])) {
|
||||
std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
|
||||
std::cerr << Colors::RED << "✖" << Colors::RESET << " Couldn't enable plugin (missing?)\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
if (ret == LOADSTATE_HYPRLAND_UPDATED)
|
||||
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] Enabled plugin, but Hyprland was updated. Please restart Hyprland.");
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
if (ret != LOADSTATE_OK)
|
||||
return 1;
|
||||
} else if (command[0] == "disable") {
|
||||
if (command.size() < 2) {
|
||||
std::println(stderr, "{}", failureString("Not enough args for disable."));
|
||||
std::cerr << Colors::RED << "✖" << Colors::RESET << " Not enough args for disable.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!g_pPluginManager->disablePlugin(command[1])) {
|
||||
std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
|
||||
std::cerr << Colors::RED << "✖" << Colors::RESET << " Couldn't disable plugin (missing?)\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
if (ret != LOADSTATE_OK)
|
||||
return 1;
|
||||
} else if (command[0] == "reload") {
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
|
||||
auto ret = g_pPluginManager->ensurePluginsLoadState();
|
||||
|
||||
if (ret != LOADSTATE_OK) {
|
||||
if (notify) {
|
||||
switch (ret) {
|
||||
case LOADSTATE_FAIL:
|
||||
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
|
||||
case LOADSTATE_HEADERS_OUTDATED:
|
||||
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins: Outdated headers. Please run hyprpm update manually.");
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (ret != LOADSTATE_OK && 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 && !notifyFail) {
|
||||
} else if (notify) {
|
||||
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
|
||||
}
|
||||
} else if (command[0] == "purge-cache") {
|
||||
NSys::root::cacheSudo();
|
||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||
DataState::purgeAllCache();
|
||||
} else if (command[0] == "list") {
|
||||
g_pPluginManager->listAllPlugins();
|
||||
} else {
|
||||
std::println(stderr, "{}", HELP);
|
||||
std::cout << HELP;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -8,7 +8,6 @@ executable(
|
||||
dependency('hyprutils', version: '>= 0.1.1'),
|
||||
dependency('threads'),
|
||||
dependency('tomlplusplus'),
|
||||
dependency('glaze', method: 'cmake'),
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
|
@@ -1,80 +1,80 @@
|
||||
#include "CProgressBar.hpp"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <format>
|
||||
#include <print>
|
||||
#include <cstdio>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../helpers/Colors.hpp"
|
||||
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static winsize getTerminalSize() {
|
||||
winsize w{};
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
return w;
|
||||
}
|
||||
|
||||
static void clearCurrentLine() {
|
||||
std::print("\r\33[2K"); // ansi escape sequence to clear entire line
|
||||
}
|
||||
|
||||
void CProgressBar::printMessageAbove(const std::string& msg) {
|
||||
clearCurrentLine();
|
||||
std::print("\r{}\n", msg);
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
print(); // reprint bar underneath
|
||||
std::string spaces;
|
||||
for (size_t i = 0; i < w.ws_col; ++i) {
|
||||
spaces += ' ';
|
||||
}
|
||||
|
||||
std::cout << "\r" << spaces << "\r" << msg << "\n";
|
||||
print();
|
||||
}
|
||||
|
||||
void CProgressBar::print() {
|
||||
const auto w = getTerminalSize();
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
|
||||
if (m_bFirstPrint) {
|
||||
std::print("\n");
|
||||
m_bFirstPrint = false;
|
||||
if (m_bFirstPrint)
|
||||
std::cout << "\n";
|
||||
m_bFirstPrint = false;
|
||||
|
||||
std::string spaces;
|
||||
for (size_t i = 0; i < w.ws_col; ++i) {
|
||||
spaces += ' ';
|
||||
}
|
||||
|
||||
clearCurrentLine();
|
||||
std::cout << "\r" << spaces << "\r";
|
||||
|
||||
float percentDone = 0.0f;
|
||||
if (m_fPercentage >= 0.0f)
|
||||
std::string message = "";
|
||||
|
||||
float percentDone = 0;
|
||||
if (m_fPercentage >= 0)
|
||||
percentDone = m_fPercentage;
|
||||
else {
|
||||
// check for divide-by-zero
|
||||
percentDone = m_iMaxSteps > 0 ? sc<float>(m_iSteps) / m_iMaxSteps : 0.0f;
|
||||
else
|
||||
percentDone = (float)m_iSteps / (float)m_iMaxSteps;
|
||||
|
||||
const auto BARWIDTH = std::clamp(w.ws_col - static_cast<unsigned long>(m_szCurrentMessage.length()) - 2, 0UL, 50UL);
|
||||
|
||||
// draw bar
|
||||
message += std::string{" "} + Colors::GREEN;
|
||||
size_t i = 0;
|
||||
for (; i < std::floor(percentDone * BARWIDTH); ++i) {
|
||||
message += "━";
|
||||
}
|
||||
// clamp to ensure no overflows (sanity check)
|
||||
percentDone = std::clamp(percentDone, 0.0f, 1.0f);
|
||||
|
||||
const size_t BARWIDTH = std::clamp<size_t>(w.ws_col - m_szCurrentMessage.length() - 2, 0, 50);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << ' ' << Colors::GREEN;
|
||||
|
||||
size_t filled = std::floor(percentDone * BARWIDTH);
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < filled; ++i)
|
||||
oss << "━";
|
||||
|
||||
if (i < BARWIDTH) {
|
||||
oss << "╍" << Colors::RESET;
|
||||
++i;
|
||||
for (; i < BARWIDTH; ++i)
|
||||
oss << "━";
|
||||
i++;
|
||||
|
||||
message += std::string{"╍"} + Colors::RESET;
|
||||
|
||||
for (; i < BARWIDTH; ++i) {
|
||||
message += "━";
|
||||
}
|
||||
} else
|
||||
oss << Colors::RESET;
|
||||
message += Colors::RESET;
|
||||
|
||||
if (m_fPercentage >= 0.0f)
|
||||
oss << " " << std::format("{}%", sc<int>(percentDone * 100.0)) << ' ';
|
||||
// draw progress
|
||||
if (m_fPercentage >= 0)
|
||||
message += " " + std::format("{}%", static_cast<int>(percentDone * 100.0)) + " ";
|
||||
else
|
||||
oss << " " << std::format("{} / {}", m_iSteps, m_iMaxSteps) << ' ';
|
||||
message += " " + std::format("{} / {}", m_iSteps, m_iMaxSteps) + " ";
|
||||
|
||||
// draw message
|
||||
std::cout << message + " " + m_szCurrentMessage;
|
||||
|
||||
std::print("{} {}", oss.str(), m_szCurrentMessage);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
@@ -14,4 +14,4 @@ class CProgressBar {
|
||||
|
||||
private:
|
||||
bool m_bFirstPrint = true;
|
||||
};
|
||||
};
|
@@ -1,100 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
project(hyprtester DESCRIPTION "Hyprland test suite")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 26)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
pkg_check_modules(hyprtester_deps REQUIRED IMPORTED_TARGET hyprutils>=0.5.0)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
|
||||
add_executable(hyprtester ${SRCFILES})
|
||||
add_custom_command(
|
||||
TARGET hyprtester
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/plugin/build.sh
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/plugin)
|
||||
|
||||
target_link_libraries(hyprtester PUBLIC PkgConfig::hyprtester_deps)
|
||||
|
||||
install(TARGETS hyprtester)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test.conf
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/plugin/hyprtestplugin.so
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
|
||||
|
||||
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/src/tests/clients/build.hpp
|
||||
"#include <string>\n"
|
||||
"static const std::string binaryDir = \"${CMAKE_CURRENT_BINARY_DIR}\";"
|
||||
)
|
||||
|
||||
######## wayland protocols testing stuff
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
|
||||
endif()
|
||||
|
||||
find_package(hyprwayland-scanner 0.4.0 REQUIRED)
|
||||
pkg_check_modules(
|
||||
protocols_deps
|
||||
REQUIRED
|
||||
IMPORTED_TARGET
|
||||
hyprutils>=0.8.0
|
||||
wayland-client
|
||||
wayland-protocols
|
||||
)
|
||||
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
||||
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
|
||||
pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
|
||||
message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}")
|
||||
|
||||
# gen core wayland stuff
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.hpp
|
||||
COMMAND hyprwayland-scanner --wayland-enums --client
|
||||
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_CURRENT_SOURCE_DIR}/protocols/
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
function(protocolNew protoPath protoName external)
|
||||
if(external)
|
||||
set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath})
|
||||
else()
|
||||
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.hpp
|
||||
COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endfunction()
|
||||
function(clientNew sourceName)
|
||||
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "PROTOS")
|
||||
|
||||
add_executable(${sourceName} clients/${sourceName}.cpp)
|
||||
|
||||
target_include_directories(${sourceName} BEFORE PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/protocols")
|
||||
target_link_libraries(${sourceName} PUBLIC PkgConfig::protocols_deps)
|
||||
|
||||
target_sources(${sourceName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.cpp ${CMAKE_CURRENT_SOURCE_DIR}/protocols/wayland.hpp)
|
||||
|
||||
foreach(protoName IN LISTS ARG_PROTOS)
|
||||
target_sources(${sourceName} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/${protoName}.hpp)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
|
||||
protocolnew("stable/xdg-shell" "xdg-shell" false)
|
||||
|
||||
clientNew("pointer-warp" PROTOS "pointer-warp-v1" "xdg-shell")
|
@@ -1,317 +0,0 @@
|
||||
#include <cstring>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <print>
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland.hpp>
|
||||
#include <xdg-shell.hpp>
|
||||
#include <pointer-warp-v1.hpp>
|
||||
|
||||
#include <hyprutils/memory/SharedPtr.hpp>
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <hyprutils/os/FileDescriptor.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;
|
||||
CSharedPointer<CCWpPointerWarpV1> pointerWarp;
|
||||
|
||||
// shm/buffer stuff
|
||||
CSharedPointer<CCWlShmPool> shmPool;
|
||||
CSharedPointer<CCWlBuffer> shmBuf;
|
||||
int shmFd;
|
||||
size_t shmBufSize;
|
||||
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;
|
||||
};
|
||||
|
||||
static bool debug, started, shouldExit;
|
||||
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
static void clientLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::println("{}", std::vformat(fmt.get(), std::make_format_args(args...)));
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
static void debugLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||
if (!debug)
|
||||
return;
|
||||
std::println("{}", std::vformat(fmt.get(), std::make_format_args(args...)));
|
||||
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));
|
||||
} else if (NAME == "wp_pointer_warp_v1") {
|
||||
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||
state.pointerWarp = makeShared<CCWpPointerWarpV1>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wp_pointer_warp_v1_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 || !state.pointerWarp) {
|
||||
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) < 0) {
|
||||
close(state.shmFd);
|
||||
return false;
|
||||
}
|
||||
|
||||
state.shmPool = makeShared<CCWlShmPool>(state.wlShm->sendCreatePool(state.shmFd, size));
|
||||
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);
|
||||
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("pointer-warp test client");
|
||||
state.xdgToplevel->sendSetAppId("pointer-warp");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// format is like below
|
||||
// "warp 20 20\n" would ask to warp cursor to x=20,y=20 in surface local coords
|
||||
static void parseRequest(SWlState& state, std::string req) {
|
||||
if (req.contains("exit")) {
|
||||
shouldExit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req.starts_with("warp "))
|
||||
return;
|
||||
|
||||
auto it = req.find_first_of('\n');
|
||||
if (it == std::string::npos)
|
||||
return;
|
||||
|
||||
req = req.substr(0, it);
|
||||
|
||||
it = req.find_first_of(' ');
|
||||
if (it == std::string::npos)
|
||||
return;
|
||||
|
||||
req = req.substr(it + 1);
|
||||
|
||||
it = req.find_first_of(' ');
|
||||
|
||||
int x = std::stoi(req.substr(0, it));
|
||||
int y = std::stoi(req.substr(it + 1));
|
||||
|
||||
state.pointerWarp->sendWarpPointer(state.surf->resource(), state.pointer->resource(), wl_fixed_from_int(x), wl_fixed_from_int(y), state.enterSerial);
|
||||
|
||||
// sync the request then reply
|
||||
wl_display_roundtrip(state.display);
|
||||
|
||||
clientLog("parsed request to move to x:{}, y:{}", x, y);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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()});
|
||||
}
|
||||
}
|
||||
|
||||
wl_display* display = state.display;
|
||||
state = {};
|
||||
|
||||
wl_display_disconnect(display);
|
||||
return 0;
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
CXXFLAGS = -shared -fPIC -g -std=c++2b -Wno-c++11-narrowing
|
||||
INCLUDES = `pkg-config --cflags pixman-1 libdrm pangocairo libinput libudev wayland-server xkbcommon`
|
||||
LIBS = `pkg-config --libs pangocairo`
|
||||
|
||||
SRC = src/main.cpp
|
||||
TARGET = hyprtestplugin.so
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) -I../.. -I../../protocols $(INCLUDES) $^ $> -o $@ $(LIBS) -O2
|
||||
|
||||
clean:
|
||||
rm -f ./$(TARGET)
|
||||
|
||||
.PHONY: all clean
|
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
make clean
|
||||
make all
|
@@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <src/plugins/PluginAPI.hpp>
|
||||
|
||||
inline HANDLE PHANDLE = nullptr;
|
@@ -1,190 +0,0 @@
|
||||
#include <unistd.h>
|
||||
#include <src/includes.hpp>
|
||||
#include <sstream>
|
||||
#include <any>
|
||||
|
||||
#define private public
|
||||
#include <src/config/ConfigManager.hpp>
|
||||
#include <src/config/ConfigDescriptions.hpp>
|
||||
#include <src/layout/IHyprLayout.hpp>
|
||||
#include <src/managers/LayoutManager.hpp>
|
||||
#include <src/managers/input/InputManager.hpp>
|
||||
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
||||
#include <src/Compositor.hpp>
|
||||
#undef private
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
using namespace Hyprutils::Utils;
|
||||
using namespace Hyprutils::String;
|
||||
|
||||
#include "globals.hpp"
|
||||
|
||||
// Do NOT change this function.
|
||||
APICALL EXPORT std::string PLUGIN_API_VERSION() {
|
||||
return HYPRLAND_API_VERSION;
|
||||
}
|
||||
|
||||
static SDispatchResult test(std::string in) {
|
||||
bool success = true;
|
||||
std::string errors = "";
|
||||
|
||||
if (g_pConfigManager->m_configValueNumber != CONFIG_OPTIONS.size() + 1 /* autogenerated is special */) {
|
||||
errors += "config value number mismatches descriptions size\n";
|
||||
success = false;
|
||||
}
|
||||
|
||||
return SDispatchResult{
|
||||
.success = success,
|
||||
.error = errors,
|
||||
};
|
||||
}
|
||||
|
||||
// Trigger a snap move event for the active window
|
||||
static SDispatchResult snapMove(std::string in) {
|
||||
const auto PLASTWINDOW = g_pCompositor->m_lastWindow.lock();
|
||||
if (!PLASTWINDOW->m_isFloating)
|
||||
return {.success = false, .error = "Window must be floating"};
|
||||
|
||||
Vector2D pos = PLASTWINDOW->m_realPosition->goal();
|
||||
Vector2D size = PLASTWINDOW->m_realSize->goal();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->performSnap(pos, size, PLASTWINDOW, MBIND_MOVE, -1, size);
|
||||
*PLASTWINDOW->m_realPosition = pos.round();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
class CTestKeyboard : public IKeyboard {
|
||||
public:
|
||||
static SP<CTestKeyboard> create(bool isVirtual) {
|
||||
auto keeb = SP<CTestKeyboard>(new CTestKeyboard());
|
||||
keeb->m_self = keeb;
|
||||
keeb->m_isVirtual = isVirtual;
|
||||
keeb->m_shareStates = !isVirtual;
|
||||
return keeb;
|
||||
}
|
||||
|
||||
virtual bool isVirtual() {
|
||||
return m_isVirtual;
|
||||
}
|
||||
|
||||
virtual SP<Aquamarine::IKeyboard> aq() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void sendKey(uint32_t key, bool pressed) {
|
||||
auto event = IKeyboard::SKeyEvent{
|
||||
.timeMs = sc<uint32_t>(Time::millis(Time::steadyNow())),
|
||||
.keycode = key,
|
||||
.state = pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
};
|
||||
updatePressed(event.keycode, pressed);
|
||||
m_keyboardEvents.key.emit(event);
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
m_events.destroy.emit();
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isVirtual;
|
||||
};
|
||||
|
||||
static SDispatchResult pressAlt(std::string in) {
|
||||
g_pInputManager->m_lastMods = in == "1" ? HL_MODIFIER_ALT : 0;
|
||||
|
||||
return {.success = true};
|
||||
}
|
||||
|
||||
static SDispatchResult simulateGesture(std::string in) {
|
||||
CVarList data(in);
|
||||
|
||||
uint32_t fingers = 3;
|
||||
try {
|
||||
fingers = std::stoul(data[1]);
|
||||
} catch (...) { return {.success = false}; }
|
||||
|
||||
if (data[0] == "down") {
|
||||
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
|
||||
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {0, 300}});
|
||||
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
|
||||
} else if (data[0] == "up") {
|
||||
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
|
||||
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {0, -300}});
|
||||
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
|
||||
} else if (data[0] == "left") {
|
||||
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
|
||||
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {-300, 0}});
|
||||
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
|
||||
} else {
|
||||
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
|
||||
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {300, 0}});
|
||||
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
|
||||
}
|
||||
|
||||
return {.success = true};
|
||||
}
|
||||
|
||||
static SDispatchResult vkb(std::string in) {
|
||||
auto tkb0 = CTestKeyboard::create(false);
|
||||
auto tkb1 = CTestKeyboard::create(false);
|
||||
auto vkb0 = CTestKeyboard::create(true);
|
||||
|
||||
g_pInputManager->newKeyboard(tkb0);
|
||||
g_pInputManager->newKeyboard(tkb1);
|
||||
g_pInputManager->newKeyboard(vkb0);
|
||||
|
||||
CScopeGuard x([&] {
|
||||
tkb0->destroy();
|
||||
tkb1->destroy();
|
||||
vkb0->destroy();
|
||||
});
|
||||
|
||||
const auto& PRESSED = g_pInputManager->getKeysFromAllKBs();
|
||||
const uint32_t TESTKEY = 1;
|
||||
|
||||
tkb0->sendKey(TESTKEY, true);
|
||||
if (!std::ranges::contains(PRESSED, TESTKEY)) {
|
||||
return {
|
||||
.success = false,
|
||||
.error = "Expected pressed key not found",
|
||||
};
|
||||
}
|
||||
|
||||
tkb1->sendKey(TESTKEY, true);
|
||||
tkb0->sendKey(TESTKEY, false);
|
||||
if (!std::ranges::contains(PRESSED, TESTKEY)) {
|
||||
return {
|
||||
.success = false,
|
||||
.error = "Expected pressed key not found (kb share state)",
|
||||
};
|
||||
}
|
||||
|
||||
vkb0->sendKey(TESTKEY, true);
|
||||
tkb1->sendKey(TESTKEY, false);
|
||||
if (std::ranges::contains(PRESSED, TESTKEY)) {
|
||||
return {
|
||||
.success = false,
|
||||
.error = "Expected released key found in pressed (vkb no share state)",
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||
PHANDLE = handle;
|
||||
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:test", ::test);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:snapmove", ::snapMove);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:vkb", ::vkb);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture);
|
||||
|
||||
return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"};
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT() {
|
||||
;
|
||||
}
|
2
hyprtester/protocols/.gitignore
vendored
2
hyprtester/protocols/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <format>
|
||||
#include <print>
|
||||
|
||||
namespace NLog {
|
||||
template <typename... Args>
|
||||
//NOLINTNEXTLINE
|
||||
void log(std::format_string<Args...> fmt, Args&&... args) {
|
||||
std::string logMsg = "";
|
||||
|
||||
logMsg += std::vformat(fmt.get(), std::make_format_args(args...));
|
||||
|
||||
std::println("{}", logMsg);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
@@ -1,138 +0,0 @@
|
||||
#include "hyprctlCompat.hpp"
|
||||
#include "shared.hpp"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <print>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static int getUID() {
|
||||
const auto UID = getuid();
|
||||
const auto PWUID = getpwuid(UID);
|
||||
return PWUID ? PWUID->pw_uid : UID;
|
||||
}
|
||||
|
||||
static std::string getRuntimeDir() {
|
||||
const auto XDG = getenv("XDG_RUNTIME_DIR");
|
||||
|
||||
if (!XDG) {
|
||||
const std::string USERID = std::to_string(getUID());
|
||||
return "/run/user/" + USERID + "/hypr";
|
||||
}
|
||||
|
||||
return std::string{XDG} + "/hypr";
|
||||
}
|
||||
|
||||
std::vector<SInstanceData> instances() {
|
||||
std::vector<SInstanceData> result;
|
||||
|
||||
try {
|
||||
if (!std::filesystem::exists(getRuntimeDir()))
|
||||
return {};
|
||||
} catch (std::exception& e) { return {}; }
|
||||
|
||||
for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) {
|
||||
if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock"))
|
||||
continue;
|
||||
|
||||
// read lock
|
||||
SInstanceData* data = &result.emplace_back();
|
||||
data->id = el.path().filename().string();
|
||||
|
||||
try {
|
||||
data->time = std::stoull(data->id.substr(data->id.find_first_of('_') + 1, data->id.find_last_of('_') - (data->id.find_first_of('_') + 1)));
|
||||
} catch (std::exception& e) { continue; }
|
||||
|
||||
// read file
|
||||
std::ifstream ifs(el.path().string() + "/hyprland.lock");
|
||||
|
||||
int i = 0;
|
||||
for (std::string line; std::getline(ifs, line); ++i) {
|
||||
if (i == 0) {
|
||||
try {
|
||||
data->pid = std::stoull(line);
|
||||
} catch (std::exception& e) { continue; }
|
||||
} else if (i == 1) {
|
||||
data->wlSocket = line;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
std::erase_if(result, [&](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; });
|
||||
|
||||
std::sort(result.begin(), result.end(), [&](const auto& a, const auto& b) { return a.time < b.time; });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string getFromSocket(const std::string& cmd) {
|
||||
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
auto t = timeval{.tv_sec = 5, .tv_usec = 0};
|
||||
setsockopt(SERVERSOCKET, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval));
|
||||
|
||||
if (SERVERSOCKET < 0) {
|
||||
std::println("socket: Couldn't open a socket (1)");
|
||||
return "";
|
||||
}
|
||||
|
||||
sockaddr_un serverAddress = {0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
std::string socketPath = getRuntimeDir() + "/" + HIS + "/.socket.sock";
|
||||
|
||||
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(SERVERSOCKET, rc<sockaddr*>(&serverAddress), SUN_LEN(&serverAddress)) < 0) {
|
||||
std::println("Couldn't connect to {}. (3)", socketPath);
|
||||
return "";
|
||||
}
|
||||
|
||||
auto sizeWritten = write(SERVERSOCKET, cmd.c_str(), cmd.length());
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
std::println("Couldn't write (4)");
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string reply = "";
|
||||
char buffer[8192] = {0};
|
||||
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
|
||||
if (sizeWritten < 0) {
|
||||
if (errno == EWOULDBLOCK)
|
||||
std::println("Hyprland IPC didn't respond in time");
|
||||
std::println("Couldn't read (5)");
|
||||
return "";
|
||||
}
|
||||
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
|
||||
while (sizeWritten == 8192) {
|
||||
sizeWritten = read(SERVERSOCKET, buffer, 8192);
|
||||
if (sizeWritten < 0) {
|
||||
std::println("Couldn't read (5)");
|
||||
return "";
|
||||
}
|
||||
reply += std::string(buffer, sizeWritten);
|
||||
}
|
||||
|
||||
close(SERVERSOCKET);
|
||||
|
||||
return reply;
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
struct SInstanceData {
|
||||
std::string id;
|
||||
uint64_t time;
|
||||
uint64_t pid;
|
||||
std::string wlSocket;
|
||||
bool valid = true;
|
||||
};
|
||||
|
||||
std::vector<SInstanceData> instances();
|
||||
std::string getFromSocket(const std::string& cmd);
|
@@ -1,261 +0,0 @@
|
||||
|
||||
// This is a tester for Hyprland. It will launch the built binary in ./build/Hyprland
|
||||
// in headless mode and test various things.
|
||||
// for now it's quite basic and limited, but will be expanded in the future.
|
||||
|
||||
// NOTE: This tester has to be ran from its directory!!
|
||||
|
||||
// Some TODO:
|
||||
// - Add a plugin built alongside so that we can do more detailed tests (e.g. simulating keystrokes)
|
||||
// - test coverage
|
||||
// - maybe figure out a way to do some visual tests too?
|
||||
|
||||
// Required runtime deps for checks:
|
||||
// - kitty
|
||||
// - xeyes
|
||||
|
||||
#include "shared.hpp"
|
||||
#include "hyprctlCompat.hpp"
|
||||
#include "tests/main/tests.hpp"
|
||||
#include "tests/clients/tests.hpp"
|
||||
#include "tests/plugin/plugin.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/memory/Casts.hpp>
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <print>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
|
||||
#include "Log.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define SP CSharedPointer
|
||||
|
||||
static int ret = 0;
|
||||
static SP<CProcess> hyprlandProc;
|
||||
static const std::string cwd = std::filesystem::current_path().string();
|
||||
|
||||
//
|
||||
static bool launchHyprland(std::string configPath, std::string binaryPath) {
|
||||
if (binaryPath == "") {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(cwd + "/../build/Hyprland", ec) || ec) {
|
||||
NLog::log("{}No Hyprland binary", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
binaryPath = cwd + "/../build/Hyprland";
|
||||
}
|
||||
|
||||
if (configPath == "") {
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(cwd + "/test.conf", ec) || ec) {
|
||||
NLog::log("{}No test config", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
configPath = cwd + "/test.conf";
|
||||
}
|
||||
|
||||
NLog::log("{}Launching Hyprland", Colors::YELLOW);
|
||||
hyprlandProc = makeShared<CProcess>(binaryPath, std::vector<std::string>{"--config", configPath});
|
||||
hyprlandProc->addEnv("HYPRLAND_HEADLESS_ONLY", "1");
|
||||
|
||||
NLog::log("{}Launched async process", Colors::YELLOW);
|
||||
|
||||
return hyprlandProc->runAsync();
|
||||
}
|
||||
|
||||
static bool hyprlandAlive() {
|
||||
NLog::log("{}hyprlandAlive", Colors::YELLOW);
|
||||
kill(hyprlandProc->pid(), 0);
|
||||
return errno != ESRCH;
|
||||
}
|
||||
|
||||
static void help() {
|
||||
NLog::log("usage: hyprtester [arg [...]].\n");
|
||||
NLog::log(R"(Arguments:
|
||||
--help -h - Show this message again
|
||||
--config FILE -c FILE - Specify config file to use
|
||||
--binary FILE -b FILE - Specify Hyprland binary to use
|
||||
--plugin FILE -p FILE - Specify the location of the test plugin)");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
|
||||
std::string configPath = "";
|
||||
std::string binaryPath = "";
|
||||
std::string pluginPath = std::filesystem::current_path().string();
|
||||
|
||||
if (argc > 1) {
|
||||
std::span<char*> args{argv + 1, sc<std::size_t>(argc - 1)};
|
||||
|
||||
for (auto it = args.begin(); it != args.end(); it++) {
|
||||
std::string_view value = *it;
|
||||
|
||||
if (value == "--config" || value == "-c") {
|
||||
if (std::next(it) == args.end()) {
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
configPath = *std::next(it);
|
||||
|
||||
try {
|
||||
configPath = std::filesystem::canonical(configPath);
|
||||
|
||||
if (!std::filesystem::is_regular_file(configPath)) {
|
||||
throw std::exception();
|
||||
}
|
||||
} catch (...) {
|
||||
std::println(stderr, "[ ERROR ] Config file '{}' doesn't exist!", configPath);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
it++;
|
||||
|
||||
continue;
|
||||
} else if (value == "--binary" || value == "-b") {
|
||||
if (std::next(it) == args.end()) {
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
binaryPath = *std::next(it);
|
||||
|
||||
try {
|
||||
binaryPath = std::filesystem::canonical(binaryPath);
|
||||
|
||||
if (!std::filesystem::is_regular_file(binaryPath)) {
|
||||
throw std::exception();
|
||||
}
|
||||
} catch (...) {
|
||||
std::println(stderr, "[ ERROR ] Binary '{}' doesn't exist!", binaryPath);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
it++;
|
||||
|
||||
continue;
|
||||
} else if (value == "--plugin" || value == "-p") {
|
||||
if (std::next(it) == args.end()) {
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
pluginPath = *std::next(it);
|
||||
|
||||
try {
|
||||
pluginPath = std::filesystem::canonical(pluginPath);
|
||||
|
||||
if (!std::filesystem::is_regular_file(pluginPath)) {
|
||||
throw std::exception();
|
||||
}
|
||||
} catch (...) {
|
||||
std::println(stderr, "[ ERROR ] plugin '{}' doesn't exist!", pluginPath);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
it++;
|
||||
|
||||
continue;
|
||||
} else if (value == "--help" || value == "-h") {
|
||||
help();
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
std::println(stderr, "[ ERROR ] Unknown option '{}' !", *it);
|
||||
help();
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NLog::log("{}launching hl", Colors::YELLOW);
|
||||
if (!launchHyprland(configPath, binaryPath)) {
|
||||
NLog::log("{}well it failed", Colors::RED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// hyprland has launched, let's check if it's alive after 10s
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
|
||||
NLog::log("{}slept for 10s", Colors::YELLOW);
|
||||
if (!hyprlandAlive()) {
|
||||
NLog::log("{}Hyprland failed to launch", Colors::RED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// wonderful, we are in. Let's get the instance signature.
|
||||
NLog::log("{}trying to get INSTANCES", Colors::YELLOW);
|
||||
const auto INSTANCES = instances();
|
||||
if (INSTANCES.empty()) {
|
||||
NLog::log("{}Hyprland failed to launch (2)", Colors::RED);
|
||||
return 1;
|
||||
}
|
||||
|
||||
HIS = INSTANCES.back().id;
|
||||
WLDISPLAY = INSTANCES.back().wlSocket;
|
||||
|
||||
NLog::log("{}trying to get create headless output", Colors::YELLOW);
|
||||
getFromSocket("/output create headless");
|
||||
|
||||
NLog::log("{}trying to load plugin", Colors::YELLOW);
|
||||
if (const auto R = getFromSocket(std::format("/plugin load {}", pluginPath)); R != "ok") {
|
||||
NLog::log("{}Failed to load the test plugin: {}", Colors::RED, R);
|
||||
getFromSocket("/dispatch exit 1");
|
||||
return 1;
|
||||
}
|
||||
|
||||
NLog::log("{}Loaded plugin", Colors::YELLOW);
|
||||
|
||||
NLog::log("{}Running main tests", Colors::YELLOW);
|
||||
|
||||
for (const auto& fn : testFns) {
|
||||
EXPECT(fn(), true);
|
||||
}
|
||||
|
||||
NLog::log("{}Running protocol client tests", Colors::YELLOW);
|
||||
|
||||
for (const auto& fn : clientTestFns) {
|
||||
EXPECT(fn(), true);
|
||||
}
|
||||
|
||||
NLog::log("{}running plugin test", Colors::YELLOW);
|
||||
EXPECT(testPlugin(), true);
|
||||
|
||||
NLog::log("{}running vkb test from plugin", Colors::YELLOW);
|
||||
EXPECT(testVkb(), true);
|
||||
|
||||
// kill hyprland
|
||||
NLog::log("{}dispatching exit", Colors::YELLOW);
|
||||
getFromSocket("/dispatch exit");
|
||||
|
||||
NLog::log("\n{}Summary:\n\tPASSED: {}{}{}/{}\n\tFAILED: {}{}{}/{}\n{}", Colors::RESET, Colors::GREEN, TESTS_PASSED, Colors::RESET, TESTS_PASSED + TESTS_FAILED, Colors::RED,
|
||||
TESTS_FAILED, Colors::RESET, TESTS_PASSED + TESTS_FAILED, (TESTS_FAILED > 0 ? std::string{Colors::RED} + "\nSome tests failed.\n" : ""));
|
||||
|
||||
kill(hyprlandProc->pid(), SIGKILL);
|
||||
|
||||
hyprlandProc.reset();
|
||||
|
||||
return ret || TESTS_FAILED;
|
||||
}
|
@@ -1,90 +0,0 @@
|
||||
// Stolen from hyprutils
|
||||
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
inline std::string HIS = "";
|
||||
inline std::string WLDISPLAY = "";
|
||||
inline int TESTS_PASSED = 0;
|
||||
inline int TESTS_FAILED = 0;
|
||||
|
||||
namespace Colors {
|
||||
constexpr const char* RED = "\x1b[31m";
|
||||
constexpr const char* GREEN = "\x1b[32m";
|
||||
constexpr const char* YELLOW = "\x1b[33m";
|
||||
constexpr const char* BLUE = "\x1b[34m";
|
||||
constexpr const char* MAGENTA = "\x1b[35m";
|
||||
constexpr const char* CYAN = "\x1b[36m";
|
||||
constexpr const char* RESET = "\x1b[0m";
|
||||
};
|
||||
|
||||
#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__); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, val); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_VECTOR2D(expr, val) \
|
||||
do { \
|
||||
const auto& RESULT = expr; \
|
||||
const auto& EXPECTED = val; \
|
||||
if (!(std::abs(RESULT.x - EXPECTED.x) < 1e-6 && std::abs(RESULT.y - EXPECTED.y) < 1e-6)) { \
|
||||
NLog::log("{}Failed: {}{}, expected [{}, {}], got [{}, {}]. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, EXPECTED.x, EXPECTED.y, RESULT.x, RESULT.y, __FILE__, \
|
||||
__LINE__); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{}. Got [{}, {}].", Colors::GREEN, Colors::RESET, #expr, RESULT.x, RESULT.y); \
|
||||
TESTS_PASSED++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_CONTAINS(haystack, needle) \
|
||||
if (const auto EXPECTED = needle; !std::string{haystack}.contains(EXPECTED)) { \
|
||||
NLog::log("{}Failed: {}{} should contain {} but doesn't. Source: {}@{}. Haystack is:\n{}", Colors::RED, Colors::RESET, #haystack, #needle, __FILE__, __LINE__, \
|
||||
std::string{haystack}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} contains {}.", Colors::GREEN, Colors::RESET, #haystack, EXPECTED); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_NOT_CONTAINS(haystack, needle) \
|
||||
if (std::string{haystack}.contains(needle)) { \
|
||||
NLog::log("{}Failed: {}{} shouldn't contain {} but does. Source: {}@{}. Haystack is:\n{}", Colors::RED, Colors::RESET, #haystack, #needle, __FILE__, __LINE__, \
|
||||
std::string{haystack}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} doesn't contain {}.", Colors::GREEN, Colors::RESET, #haystack, #needle); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_STARTS_WITH(str, what) \
|
||||
if (!std::string{str}.starts_with(what)) { \
|
||||
NLog::log("{}Failed: {}{} should start with {} but doesn't. Source: {}@{}. String is:\n{}", Colors::RED, Colors::RESET, #str, #what, __FILE__, __LINE__, \
|
||||
std::string{str}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} starts with {}.", Colors::GREEN, Colors::RESET, #str, #what); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define EXPECT_COUNT_STRING(str, what, no) \
|
||||
if (Tests::countOccurrences(str, what) != no) { \
|
||||
NLog::log("{}Failed: {}{} should contain {} {} times, but doesn't. Source: {}@{}. String is:\n{}", Colors::RED, Colors::RESET, #str, #what, no, __FILE__, __LINE__, \
|
||||
std::string{str}); \
|
||||
ret = 1; \
|
||||
TESTS_FAILED++; \
|
||||
} else { \
|
||||
NLog::log("{}Passed: {}{} contains {} {} times.", Colors::GREEN, Colors::RESET, #str, #what, no); \
|
||||
TESTS_PASSED++; \
|
||||
}
|
||||
|
||||
#define OK(x) EXPECT(x, "ok")
|
1
hyprtester/src/tests/clients/.gitignore
vendored
1
hyprtester/src/tests/clients/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
build.hpp
|
@@ -1,180 +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;
|
||||
|
||||
static bool startClient(SClient& client) {
|
||||
client.proc = makeShared<CProcess>(binaryDir + "/pointer-warp", std::vector<std::string>{});
|
||||
|
||||
client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
||||
|
||||
int pipeFds1[2], pipeFds2[2];
|
||||
if (pipe(pipeFds1) != 0 || pipe(pipeFds2) != 0) {
|
||||
NLog::log("{}Unable to open pipe to client", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
client.writeFd = CFileDescriptor(pipeFds1[1]);
|
||||
client.proc->setStdinFD(pipeFds1[0]);
|
||||
|
||||
client.readFd = CFileDescriptor(pipeFds2[0]);
|
||||
client.proc->setStdoutFD(pipeFds2[1]);
|
||||
|
||||
client.proc->runAsync();
|
||||
|
||||
close(pipeFds1[0]);
|
||||
close(pipeFds2[1]);
|
||||
|
||||
client.fds = {.fd = client.readFd.get(), .events = POLLIN};
|
||||
if (poll(&client.fds, 1, 1000) != 1 || !(client.fds.revents & POLLIN))
|
||||
return false;
|
||||
|
||||
client.readBuf.fill(0);
|
||||
if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1)
|
||||
return false;
|
||||
|
||||
std::string ret = std::string{client.readBuf.data()};
|
||||
if (ret.find("started") == std::string::npos) {
|
||||
NLog::log("{}Failed to start pointer-warp client, read {}", Colors::RED, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait for window to appear
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
|
||||
|
||||
if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") {
|
||||
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getFromSocket(std::format("/dispatch focuswindow pid:{}", client.proc->pid())) != "ok") {
|
||||
NLog::log("{}Failed to focus pointer-warp client", Colors::RED, ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Started pointer-warp 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();
|
||||
}
|
||||
|
||||
// format is like below
|
||||
// "warp 20 20\n" would ask to warp cursor to x=20,y=20 in surface local coords
|
||||
static bool sendWarp(SClient& client, int x, int y) {
|
||||
std::string cmd = std::format("warp {} {}\n", x, y);
|
||||
if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length())
|
||||
return false;
|
||||
|
||||
if (poll(&client.fds, 1, 1500) != 1 || !(client.fds.revents & POLLIN))
|
||||
return false;
|
||||
ssize_t bytesRead = read(client.fds.fd, client.readBuf.data(), 1023);
|
||||
if (bytesRead == -1)
|
||||
return false;
|
||||
|
||||
client.readBuf[bytesRead] = 0;
|
||||
std::string recieved = std::string{client.readBuf.data()};
|
||||
recieved.pop_back();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isCursorPos(int x, int y) {
|
||||
// TODO: add a better way to do this using test plugin?
|
||||
std::string res = getFromSocket("/cursorpos");
|
||||
if (res == "error") {
|
||||
NLog::log("{}Cursorpos err'd: {}", Colors::RED, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = res.find_first_of(' ');
|
||||
if (res.at(it - 1) != ',') {
|
||||
NLog::log("{}Cursorpos err'd: {}", Colors::RED, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
int cursorX = std::stoi(res.substr(0, it - 1));
|
||||
int cursorY = std::stoi(res.substr(it + 1));
|
||||
|
||||
// somehow this is always gives 1 less than surfbox->pos()??
|
||||
res = getFromSocket("/activewindow");
|
||||
it = res.find("at: ") + 4;
|
||||
res = res.substr(it, res.find_first_of('\n', it) - it);
|
||||
|
||||
it = res.find_first_of(',');
|
||||
int clientX = cursorX - std::stoi(res.substr(0, it)) + 1;
|
||||
int clientY = cursorY - std::stoi(res.substr(it + 1)) + 1;
|
||||
|
||||
return clientX == x && clientY == y;
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
SClient client;
|
||||
|
||||
if (!startClient(client))
|
||||
return false;
|
||||
|
||||
EXPECT(sendWarp(client, 100, 100), true);
|
||||
EXPECT(isCursorPos(100, 100), true);
|
||||
|
||||
EXPECT(sendWarp(client, 0, 0), true);
|
||||
EXPECT(isCursorPos(0, 0), true);
|
||||
|
||||
EXPECT(sendWarp(client, 200, 200), true);
|
||||
EXPECT(isCursorPos(200, 200), true);
|
||||
|
||||
EXPECT(sendWarp(client, 100, -100), true);
|
||||
EXPECT(isCursorPos(200, 200), true);
|
||||
|
||||
EXPECT(sendWarp(client, 234, 345), true);
|
||||
EXPECT(isCursorPos(234, 345), true);
|
||||
|
||||
EXPECT(sendWarp(client, -1, -1), true);
|
||||
EXPECT(isCursorPos(234, 345), true);
|
||||
|
||||
EXPECT(sendWarp(client, 1, -1), true);
|
||||
EXPECT(isCursorPos(234, 345), true);
|
||||
|
||||
EXPECT(sendWarp(client, 13, 37), true);
|
||||
EXPECT(isCursorPos(13, 37), true);
|
||||
|
||||
EXPECT(sendWarp(client, -100, 100), true);
|
||||
EXPECT(isCursorPos(13, 37), true);
|
||||
|
||||
EXPECT(sendWarp(client, -1, 1), true);
|
||||
EXPECT(isCursorPos(13, 37), true);
|
||||
|
||||
stopClient(client);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
REGISTER_CLIENT_TEST_FN(test);
|
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
inline std::vector<std::function<bool()>> clientTestFns;
|
||||
|
||||
#define REGISTER_CLIENT_TEST_FN(fn) \
|
||||
static auto _register_fn = [] { \
|
||||
clientTestFns.emplace_back(fn); \
|
||||
return 1; \
|
||||
}();
|
@@ -1,22 +0,0 @@
|
||||
#include "../../Log.hpp"
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing animations", Colors::GREEN);
|
||||
|
||||
auto str = getFromSocket("/animations");
|
||||
NLog::log("{}Testing bezier curve output from `hyprctl animations`", Colors::YELLOW);
|
||||
{EXPECT_CONTAINS(str, std::format("beziers:\n\n\tname: quick\n\t\tX0: 0.15\n\t\tY0: 0.00\n\t\tX1: 0.10\n\t\tY1: 1.00"))};
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,155 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#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 gestures", Colors::GREEN);
|
||||
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace 1"); // no OK: we might be on 1 already
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture left,3"));
|
||||
|
||||
// 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);
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture right,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "floating: 1");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture down,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture down,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:alt 1"));
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture left,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "ID 2 (2)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture right,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 2 (2)");
|
||||
}
|
||||
|
||||
// check for crashes
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture right,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 2 (2)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword gestures:workspace_swipe_invert 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture right,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "ID 2 (2)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture left,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 2 (2)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword gestures:workspace_swipe_invert 1"));
|
||||
OK(getFromSocket("/keyword gestures:workspace_swipe_create_new 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture left,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 2 (2)");
|
||||
EXPECT_CONTAINS(str, "ID 1 (1)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture down,3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "floating: 0");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:alt 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:gesture up,3"));
|
||||
|
||||
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);
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// reload cfg
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,179 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#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 groups", Colors::GREEN);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Dispatching workspace `groups`", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace name:groups");
|
||||
|
||||
NLog::log("{}Spawning kittyProcA", Colors::YELLOW);
|
||||
auto kittyProcA = Tests::spawnKitty();
|
||||
if (!kittyProcA) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 1 window", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
// check kitty properties. One kitty should take the entire screen, minus the gaps.
|
||||
NLog::log("{}Check kitty dimensions", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_COUNT_STRING(str, "at: 22,22", 1);
|
||||
EXPECT_COUNT_STRING(str, "size: 1876,1036", 1);
|
||||
EXPECT_COUNT_STRING(str, "fullscreen: 0", 1);
|
||||
}
|
||||
|
||||
// group the kitty
|
||||
NLog::log("{}Enable group and groupbar", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch togglegroup"));
|
||||
OK(getFromSocket("/keyword group:groupbar:enabled 1"));
|
||||
|
||||
// check the height of the window now
|
||||
NLog::log("{}Recheck kitty dimensions", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "at: 22,43");
|
||||
EXPECT_CONTAINS(str, "size: 1876,1015");
|
||||
}
|
||||
|
||||
// disable the groupbar for ease of testing for now
|
||||
NLog::log("{}Disable groupbar", Colors::YELLOW);
|
||||
OK(getFromSocket("r/keyword group:groupbar:enabled 0"));
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Kill windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Spawn kitty again", Colors::YELLOW);
|
||||
kittyProcA = Tests::spawnKitty();
|
||||
if (!kittyProcA) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Group kitty", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch togglegroup"));
|
||||
|
||||
// check the height of the window now
|
||||
NLog::log("{}Check kitty dimensions 2", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "at: 22,22");
|
||||
EXPECT_CONTAINS(str, "size: 1876,1036");
|
||||
}
|
||||
|
||||
NLog::log("{}Spawn kittyProcB", Colors::YELLOW);
|
||||
auto kittyProcB = Tests::spawnKitty();
|
||||
if (!kittyProcB) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 2 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 2);
|
||||
|
||||
size_t lastActiveKittyIdx = 0;
|
||||
|
||||
NLog::log("{}Get last active kitty id", Colors::YELLOW);
|
||||
try {
|
||||
auto str = getFromSocket("/activewindow");
|
||||
lastActiveKittyIdx = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
||||
} catch (...) {
|
||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
// test cycling through
|
||||
|
||||
NLog::log("{}Test cycling through grouped windows", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch changegroupactive f"));
|
||||
|
||||
try {
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT(lastActiveKittyIdx != std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16), true);
|
||||
} catch (...) {
|
||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
getFromSocket("/dispatch changegroupactive f");
|
||||
|
||||
try {
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT(lastActiveKittyIdx, std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16));
|
||||
} catch (...) {
|
||||
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
NLog::log("{}Disable autogrouping", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword group:auto_group false"));
|
||||
|
||||
NLog::log("{}Spawn kittyProcC", Colors::YELLOW);
|
||||
auto kittyProcC = Tests::spawnKitty();
|
||||
if (!kittyProcC) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 3 windows 2", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 3);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_COUNT_STRING(str, "at: 22,22", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus l"));
|
||||
OK(getFromSocket("/dispatch changegroupactive 1"));
|
||||
OK(getFromSocket("/keyword group:auto_group true"));
|
||||
OK(getFromSocket("/keyword group:insert_after_current false"));
|
||||
|
||||
NLog::log("{}Spawn kittyProcD", Colors::YELLOW);
|
||||
auto kittyProcD = Tests::spawnKitty();
|
||||
if (!kittyProcD) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Expecting 4 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 4);
|
||||
|
||||
OK(getFromSocket("/dispatch changegroupactive 3"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, std::format("pid: {}", kittyProcD->pid()));
|
||||
}
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Kill windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,163 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
#define UP CUniquePointer
|
||||
#define SP CSharedPointer
|
||||
|
||||
static std::string getCommandStdOut(std::string command) {
|
||||
CProcess process("bash", {"-c", command});
|
||||
process.addEnv("HYPRLAND_INSTANCE_SIGNATURE", HIS);
|
||||
process.runSync();
|
||||
|
||||
const std::string& stdOut = process.stdOut();
|
||||
|
||||
// Remove trailing new line
|
||||
return stdOut.substr(0, stdOut.length() - 1);
|
||||
}
|
||||
|
||||
static bool testGetprop() {
|
||||
NLog::log("{}Testing hyprctl getprop", Colors::GREEN);
|
||||
if (!Tests::spawnKitty()) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
// animationstyle
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "(unset)");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": ""})");
|
||||
getFromSocket("/dispatch setprop class:kitty animationstyle teststyle");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "teststyle");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": "teststyle"})");
|
||||
|
||||
// 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]})");
|
||||
|
||||
// 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]})");
|
||||
|
||||
// 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})");
|
||||
|
||||
// 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})");
|
||||
|
||||
// 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})");
|
||||
|
||||
// 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})");
|
||||
|
||||
// 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})");
|
||||
|
||||
// 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})");
|
||||
|
||||
// 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 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");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 10})");
|
||||
getFromSocket("/dispatch setprop class:kitty rounding 4");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "4");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})");
|
||||
|
||||
// float window properties
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "2");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 2})");
|
||||
getFromSocket("/dispatch setprop class:kitty roundingpower 1.25");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "1.25");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 1.25})");
|
||||
|
||||
// errors
|
||||
EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animationstyle"), "window not found");
|
||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found");
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing hyprctl", Colors::GREEN);
|
||||
|
||||
{
|
||||
NLog::log("{}Testing hyprctl descriptions for any json errors", Colors::GREEN);
|
||||
CProcess jqProc("bash", {"-c", "hyprctl descriptions | jq"});
|
||||
jqProc.addEnv("HYPRLAND_INSTANCE_SIGNATURE", HIS);
|
||||
jqProc.runSync();
|
||||
EXPECT(jqProc.exitCode(), 0);
|
||||
}
|
||||
|
||||
if (!testGetprop())
|
||||
return false;
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test);
|
@@ -1,71 +0,0 @@
|
||||
#include "../shared.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "tests.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static void focusMasterPrevious() {
|
||||
// setup
|
||||
NLog::log("{}Spawning 1 master and 3 slave windows", Colors::YELLOW);
|
||||
// order of windows set according to new_status = master (set in test.conf)
|
||||
for (auto const& win : {"slave1", "slave2", "slave3", "master"}) {
|
||||
if (!Tests::spawnKitty(win)) {
|
||||
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||
++TESTS_FAILED;
|
||||
ret = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
NLog::log("{}Ensuring focus is on master before testing", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch layoutmsg focusmaster master"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master");
|
||||
|
||||
// test
|
||||
NLog::log("{}Testing fallback to focusmaster auto", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/dispatch layoutmsg focusmaster previous"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave1");
|
||||
|
||||
NLog::log("{}Testing focusing from slave to master", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/dispatch layoutmsg cyclenext noloop"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave2");
|
||||
OK(getFromSocket("/dispatch layoutmsg focusmaster previous"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master");
|
||||
|
||||
NLog::log("{}Testing focusing on previous window", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/dispatch layoutmsg focusmaster previous"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave2");
|
||||
|
||||
NLog::log("{}Testing focusing back to master", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/dispatch layoutmsg focusmaster previous"));
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master");
|
||||
|
||||
// clean up
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing Master layout", Colors::GREEN);
|
||||
|
||||
// setup
|
||||
OK(getFromSocket("/dispatch workspace name:master"));
|
||||
OK(getFromSocket("/keyword general:layout master"));
|
||||
|
||||
// test
|
||||
NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN);
|
||||
focusMasterPrevious();
|
||||
|
||||
// clean up
|
||||
NLog::log("Cleaning up", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test);
|
@@ -1,144 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#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 config: misc:", Colors::GREEN);
|
||||
|
||||
NLog::log("{}Testing close_special_on_empty", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword misc:close_special_on_empty false"));
|
||||
OK(getFromSocket("/dispatch workspace special:test"));
|
||||
|
||||
Tests::spawnKitty();
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "special workspace: -");
|
||||
}
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "special workspace: -");
|
||||
}
|
||||
|
||||
Tests::spawnKitty();
|
||||
|
||||
OK(getFromSocket("/keyword misc:close_special_on_empty true"));
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_NOT_CONTAINS(str, "special workspace: -");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing new_window_takes_over_fullscreen", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
||||
|
||||
Tests::spawnKitty("kitty_A");
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
EXPECT_CONTAINS(str, "kitty_A");
|
||||
}
|
||||
|
||||
Tests::spawnKitty("kitty_B");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
EXPECT_CONTAINS(str, "kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 1"));
|
||||
|
||||
Tests::spawnKitty("kitty_C");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
EXPECT_CONTAINS(str, "kitty_C");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 2"));
|
||||
|
||||
Tests::spawnKitty("kitty_D");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
EXPECT_CONTAINS(str, "kitty_D");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
||||
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Testing exit_window_retains_fullscreen", Colors::YELLOW);
|
||||
|
||||
OK(getFromSocket("/keyword misc:exit_window_retains_fullscreen false"));
|
||||
|
||||
Tests::spawnKitty("kitty_A");
|
||||
Tests::spawnKitty("kitty_B");
|
||||
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch killwindow activewindow"));
|
||||
Tests::waitUntilWindowsN(1);
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||
}
|
||||
|
||||
Tests::spawnKitty("kitty_B");
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
OK(getFromSocket("/keyword misc:exit_window_retains_fullscreen true"));
|
||||
|
||||
OK(getFromSocket("/dispatch killwindow activewindow"));
|
||||
Tests::waitUntilWindowsN(1);
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||
}
|
||||
|
||||
// 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);
|
@@ -1,85 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#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 persistent workspaces", Colors::GREEN);
|
||||
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace 1"); // no OK: we might be on 1 already
|
||||
|
||||
OK(getFromSocket("/keyword workspace 5, monitor:HEADLESS-2, persistent:1"));
|
||||
OK(getFromSocket("/keyword workspace 6, monitor:HEADLESS-PERSISTENT-TEST, persistent:1"));
|
||||
OK(getFromSocket("/keyword workspace name:PERSIST, monitor:HEADLESS-PERSISTENT-TEST, persistent:1"));
|
||||
OK(getFromSocket("/keyword workspace name:PERSIST-2, monitor:HEADLESS-PERSISTENT-TEST, persistent:1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "ID 5 (5)");
|
||||
EXPECT_COUNT_STRING(str, "workspace ID ", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/output create headless HEADLESS-PERSISTENT-TEST"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "HEADLESS-PERSISTENT-TEST");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-PERSISTENT-TEST"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "ID 2 (2)"); // this should be automatically generated by hl
|
||||
EXPECT_CONTAINS(str, "ID 5 (5)");
|
||||
EXPECT_CONTAINS(str, "ID 6 (6)");
|
||||
EXPECT_CONTAINS(str, "(PERSIST) on monitor");
|
||||
EXPECT_CONTAINS(str, "(PERSIST-2) on monitor");
|
||||
EXPECT_COUNT_STRING(str, "workspace ID ", 6);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 5 (5)");
|
||||
EXPECT_NOT_CONTAINS(str, "ID 6 (6)");
|
||||
EXPECT_NOT_CONTAINS(str, "(PERSIST) on monitor");
|
||||
EXPECT_COUNT_STRING(str, "workspace ID ", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/output remove HEADLESS-PERSISTENT-TEST"));
|
||||
|
||||
// kill all
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// reload cfg
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,176 +0,0 @@
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "../shared.hpp"
|
||||
#include "tests.hpp"
|
||||
|
||||
using Hyprutils::Math::Vector2D;
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static bool spawnFloatingKitty() {
|
||||
if (!Tests::spawnKitty()) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
OK(getFromSocket("/dispatch setfloating active"));
|
||||
OK(getFromSocket("/dispatch resizeactive exact 100 100"));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void expectSocket(const std::string& CMD) {
|
||||
if (const auto RESULT = getFromSocket(CMD); RESULT != "ok") {
|
||||
NLog::log("{}Failed: {}getFromSocket({}), expected ok, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, CMD, RESULT, __FILE__, __LINE__);
|
||||
ret = 1;
|
||||
TESTS_FAILED++;
|
||||
} else {
|
||||
NLog::log("{}Passed: {}getFromSocket({}). Got ok", Colors::GREEN, Colors::RESET, CMD);
|
||||
TESTS_PASSED++;
|
||||
}
|
||||
}
|
||||
|
||||
static void expectSnapMove(const Vector2D FROM, const Vector2D* TO) {
|
||||
const Vector2D& A = FROM;
|
||||
const Vector2D& B = TO ? *TO : FROM;
|
||||
if (TO)
|
||||
NLog::log("{}Expecting snap to ({},{}) when window is moved to ({},{})", Colors::YELLOW, B.x, B.y, A.x, A.y);
|
||||
else
|
||||
NLog::log("{}Expecting no snap when window is moved to ({},{})", Colors::YELLOW, A.x, A.y);
|
||||
|
||||
expectSocket(std::format("/dispatch moveactive exact {} {}", A.x, A.y));
|
||||
expectSocket("/dispatch plugin:test:snapmove");
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("at: {},{}", B.x, B.y));
|
||||
}
|
||||
|
||||
static void testWindowSnap(const bool RESPECTGAPS) {
|
||||
const int BORDERSIZE = 2;
|
||||
const int WINDOWSIZE = 100;
|
||||
|
||||
const int OTHER = 500;
|
||||
const int WINDOWGAP = 8;
|
||||
const int GAPSIN = 5;
|
||||
const int GAP = (RESPECTGAPS ? 2 * GAPSIN : 0) + (2 * BORDERSIZE);
|
||||
const int END = GAP + WINDOWSIZE;
|
||||
|
||||
int x;
|
||||
Vector2D predict;
|
||||
|
||||
x = WINDOWGAP + END;
|
||||
expectSnapMove({OTHER + x, OTHER}, nullptr);
|
||||
expectSnapMove({OTHER - x, OTHER}, nullptr);
|
||||
expectSnapMove({OTHER, OTHER + x}, nullptr);
|
||||
expectSnapMove({OTHER, OTHER - x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({OTHER + x, OTHER}, &(predict = {OTHER + END, OTHER}));
|
||||
expectSnapMove({OTHER - x, OTHER}, &(predict = {OTHER - END, OTHER}));
|
||||
expectSnapMove({OTHER, OTHER + x}, &(predict = {OTHER, OTHER + END}));
|
||||
expectSnapMove({OTHER, OTHER - x}, &(predict = {OTHER, OTHER - END}));
|
||||
}
|
||||
|
||||
static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) {
|
||||
const int BORDERSIZE = 2;
|
||||
const int WINDOWSIZE = 100;
|
||||
|
||||
const int MONITORGAP = 10;
|
||||
const int GAPSOUT = 20;
|
||||
const int RESP = (RESPECTGAPS ? GAPSOUT : 0);
|
||||
const int GAP = RESP + (OVERLAP ? 0 : BORDERSIZE);
|
||||
const int END = GAP + WINDOWSIZE;
|
||||
|
||||
int x;
|
||||
Vector2D predict;
|
||||
|
||||
x = MONITORGAP + GAP;
|
||||
expectSnapMove({x, x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({x, x}, &(predict = {GAP, GAP}));
|
||||
|
||||
x = MONITORGAP + END;
|
||||
expectSnapMove({1920 - x, 1080 - x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - END, 1080 - END}));
|
||||
|
||||
// test reserved area
|
||||
const int RESERVED = 200;
|
||||
const int RGAP = RESERVED + RESP + BORDERSIZE;
|
||||
const int REND = RGAP + WINDOWSIZE;
|
||||
|
||||
x = MONITORGAP + RGAP;
|
||||
expectSnapMove({x, x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({x, x}, &(predict = {RGAP, RGAP}));
|
||||
|
||||
x = MONITORGAP + REND;
|
||||
expectSnapMove({1920 - x, 1080 - x}, nullptr);
|
||||
x -= 1;
|
||||
expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - REND, 1080 - REND}));
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing snap", Colors::GREEN);
|
||||
|
||||
// move to monitor HEADLESS-2
|
||||
NLog::log("{}Moving to monitor HEADLESS-2", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
|
||||
NLog::log("{}Adding reserved monitor area to HEADLESS-2", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword monitor HEADLESS-2,addreserved,200,200,200,200"));
|
||||
|
||||
// test on workspace "snap"
|
||||
NLog::log("{}Dispatching workspace `snap`", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace name:snap"));
|
||||
|
||||
// spawn a kitty terminal and move to (500,500)
|
||||
NLog::log("{}Spawning kittyProcA", Colors::YELLOW);
|
||||
if (!spawnFloatingKitty())
|
||||
return false;
|
||||
|
||||
NLog::log("{}Expecting 1 window", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 1);
|
||||
|
||||
NLog::log("{}Move the kitty window to (500,500)", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch moveactive exact 500 500"));
|
||||
|
||||
// spawn a second kitty terminal
|
||||
NLog::log("{}Spawning kittyProcB", Colors::YELLOW);
|
||||
if (!spawnFloatingKitty())
|
||||
return false;
|
||||
|
||||
NLog::log("{}Expecting 2 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 2);
|
||||
|
||||
NLog::log("");
|
||||
testWindowSnap(false);
|
||||
testMonitorSnap(false, false);
|
||||
|
||||
NLog::log("\n{}Turning on respect_gaps", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword general:snap:respect_gaps true"));
|
||||
testWindowSnap(true);
|
||||
testMonitorSnap(true, false);
|
||||
|
||||
NLog::log("\n{}Turning on border_overlap", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword general:snap:respect_gaps false"));
|
||||
OK(getFromSocket("/keyword general:snap:border_overlap true"));
|
||||
testMonitorSnap(false, true);
|
||||
|
||||
NLog::log("\n{}Turning on both border_overlap and respect_gaps", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword general:snap:respect_gaps true"));
|
||||
testMonitorSnap(true, true);
|
||||
|
||||
// kill all
|
||||
NLog::log("\n{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
NLog::log("{}Reloading the config", Colors::YELLOW);
|
||||
OK(getFromSocket("/reload"));
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,76 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#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 solitary clients", Colors::GREEN);
|
||||
|
||||
OK(getFromSocket("/keyword general:allow_tearing false"));
|
||||
OK(getFromSocket("/keyword render:direct_scanout 0"));
|
||||
OK(getFromSocket("/keyword cursor:no_hardware_cursors 1"));
|
||||
NLog::log("{}Expecting blocked solitary/DS/tearing", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "solitary: 0\n");
|
||||
EXPECT_CONTAINS(str, "solitaryBlockedBy: windowed mode,missing candidate");
|
||||
EXPECT_CONTAINS(str, "activelyTearing: false");
|
||||
EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,user settings,not supported by monitor,missing candidate");
|
||||
EXPECT_CONTAINS(str, "directScanoutTo: 0\n");
|
||||
EXPECT_CONTAINS(str, "directScanoutBlockedBy: user settings,software renders/cursors,missing candidate");
|
||||
}
|
||||
|
||||
// FIXME: need a reliable client with solitary opaque surface in fullscreen. kitty doesn't work all the time
|
||||
// NLog::log("{}Spawning kittyProcA", Colors::YELLOW);
|
||||
// auto kittyProcA = Tests::spawnKitty();
|
||||
|
||||
// if (!kittyProcA) {
|
||||
// NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// OK(getFromSocket("/keyword general:allow_tearing true"));
|
||||
// OK(getFromSocket("/keyword render:direct_scanout 1"));
|
||||
// NLog::log("{}", getFromSocket("/clients"));
|
||||
// OK(getFromSocket("/dispatch fullscreen"));
|
||||
// NLog::log("{}", getFromSocket("/clients"));
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
// NLog::log("{}Expecting kitty to almost pass for solitary/DS/tearing", Colors::YELLOW);
|
||||
// {
|
||||
// auto str = getFromSocket("/monitors");
|
||||
// EXPECT_NOT_CONTAINS(str, "solitary: 0\n");
|
||||
// EXPECT_CONTAINS(str, "solitaryBlockedBy: null");
|
||||
// EXPECT_CONTAINS(str, "activelyTearing: false");
|
||||
// EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor,window settings");
|
||||
// }
|
||||
|
||||
// OK(getFromSocket("/dispatch setprop active immediate 1"));
|
||||
// NLog::log("{}Expecting kitty to almost pass for tearing", Colors::YELLOW);
|
||||
// {
|
||||
// auto str = getFromSocket("/monitors");
|
||||
// EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor\n");
|
||||
// }
|
||||
|
||||
// // kill all
|
||||
// NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
// Tests::killAllWindows();
|
||||
|
||||
NLog::log("{}Reloading the config", Colors::YELLOW);
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
inline std::vector<std::function<bool()>> testFns;
|
||||
|
||||
#define REGISTER_TEST_FN(fn) \
|
||||
static auto _register_fn = [] { \
|
||||
testFns.emplace_back(fn); \
|
||||
return 1; \
|
||||
}();
|
@@ -1,121 +0,0 @@
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include "../shared.hpp"
|
||||
#include "tests.hpp"
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
static bool spawnKitty(const std::string& class_) {
|
||||
NLog::log("{}Spawning {}", Colors::YELLOW, class_);
|
||||
if (!Tests::spawnKitty(class_)) {
|
||||
NLog::log("{}Error: {} did not spawn", Colors::RED, class_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing windows", Colors::GREEN);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace `window`", Colors::YELLOW);
|
||||
getFromSocket("/dispatch workspace name:window");
|
||||
|
||||
if (!spawnKitty("kitty_A"))
|
||||
return false;
|
||||
|
||||
// check kitty properties. One kitty should take the entire screen, as this is smart gaps
|
||||
NLog::log("{}Expecting kitty_A to take up the whole screen", Colors::YELLOW);
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT(str.contains("at: 0,0"), true);
|
||||
EXPECT(str.contains("size: 1920,1080"), true);
|
||||
EXPECT(str.contains("fullscreen: 0"), true);
|
||||
}
|
||||
|
||||
NLog::log("{}Testing window split ratios", Colors::YELLOW);
|
||||
{
|
||||
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 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"));
|
||||
Tests::waitUntilWindowsN(1);
|
||||
|
||||
NLog::log("{}Inverting the split ratio", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword dwindle:default_split_ratio 0.75"));
|
||||
|
||||
if (!spawnKitty("kitty_B"))
|
||||
return false;
|
||||
|
||||
NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, PERCENT);
|
||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH2, HEIGHT));
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
NLog::log("{}Expecting kitty_A 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"));
|
||||
}
|
||||
|
||||
// open xeyes
|
||||
NLog::log("{}Spawning xeyes", Colors::YELLOW);
|
||||
getFromSocket("/dispatch exec xeyes");
|
||||
|
||||
NLog::log("{}Keep checking if xeyes spawned", Colors::YELLOW);
|
||||
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);
|
||||
|
||||
NLog::log("{}Checking props of xeyes", Colors::YELLOW);
|
||||
// check some window props of xeyes, try to tile them
|
||||
{
|
||||
auto str = getFromSocket("/clients");
|
||||
EXPECT_CONTAINS(str, "floating: 1");
|
||||
getFromSocket("/dispatch settiled class:XEyes");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
str = getFromSocket("/clients");
|
||||
EXPECT_NOT_CONTAINS(str, "floating: 1");
|
||||
}
|
||||
|
||||
// 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)
|
@@ -1,358 +0,0 @@
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#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 workspaces", Colors::GREEN);
|
||||
|
||||
EXPECT(Tests::windowCount(), 0);
|
||||
|
||||
// test on workspace "window"
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Spawning kittyProc on ws 1", Colors::YELLOW);
|
||||
auto kittyProcA = Tests::spawnKitty();
|
||||
|
||||
if (!kittyProcA) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 3", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 3"));
|
||||
|
||||
NLog::log("{}Spawning kittyProc on ws 3", Colors::YELLOW);
|
||||
auto kittyProcB = Tests::spawnKitty();
|
||||
|
||||
if (!kittyProcB) {
|
||||
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Switching to workspace +1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace +1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
// check if the other workspaces are alive
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_CONTAINS(str, "workspace ID 3 (3)");
|
||||
EXPECT_CONTAINS(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/workspaces");
|
||||
EXPECT_NOT_CONTAINS(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace m+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace m+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace -1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace -1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Switching to workspace r+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r~1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r~1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace empty", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace empty"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 2 (2)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace previous", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace name:TEST_WORKSPACE_NULL", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace name:TEST_WORKSPACE_NULL"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID -1337 (TEST_WORKSPACE_NULL)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
// add a new monitor
|
||||
NLog::log("{}Adding a new monitor", Colors::YELLOW);
|
||||
EXPECT(getFromSocket("/output create headless"), "ok")
|
||||
|
||||
// should take workspace 2
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "active workspace: 2 (2)");
|
||||
EXPECT_CONTAINS(str, "active workspace: 1 (1)");
|
||||
EXPECT_CONTAINS(str, "HEADLESS-3");
|
||||
}
|
||||
|
||||
// focus the first monitor
|
||||
OK(getFromSocket("/dispatch focusmonitor 0"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace r+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace r~2", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
OK(getFromSocket("/dispatch workspace r~2"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace m+1", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch workspace m+1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||
// no OK: this will throw an error as it should
|
||||
getFromSocket("/dispatch workspace 1");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing back_and_forth", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword binds:workspace_back_and_forth true"));
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:workspace_back_and_forth false"));
|
||||
|
||||
NLog::log("{}Testing hide_special_on_workspace_change", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword binds:hide_special_on_workspace_change true"));
|
||||
OK(getFromSocket("/dispatch workspace special:HELLO"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_CONTAINS(str, "special workspace: -");
|
||||
EXPECT_CONTAINS(str, "special:HELLO");
|
||||
}
|
||||
|
||||
// no OK: will err (it shouldn't prolly but oh well)
|
||||
getFromSocket("/dispatch workspace 3");
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/monitors");
|
||||
EXPECT_COUNT_STRING(str, "special workspace: 0 ()", 2);
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:hide_special_on_workspace_change false"));
|
||||
|
||||
NLog::log("{}Testing allow_workspace_cycles", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword binds:allow_workspace_cycles true"));
|
||||
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
OK(getFromSocket("/dispatch workspace 3"));
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 1 (1)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch workspace previous"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activeworkspace");
|
||||
EXPECT_STARTS_WITH(str, "workspace ID 3 (3)");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:allow_workspace_cycles false"));
|
||||
|
||||
OK(getFromSocket("/dispatch workspace 1"));
|
||||
|
||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||
Tests::killAllWindows();
|
||||
|
||||
// spawn 3 kitties
|
||||
NLog::log("{}Testing focus_preferred_method", Colors::YELLOW);
|
||||
OK(getFromSocket("/keyword dwindle:force_split 2"));
|
||||
Tests::spawnKitty("kitty_A");
|
||||
Tests::spawnKitty("kitty_B");
|
||||
Tests::spawnKitty("kitty_C");
|
||||
OK(getFromSocket("/keyword dwindle:force_split 0"));
|
||||
|
||||
// focus kitty 2: will be top right (dwindle)
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||
|
||||
// resize it to be a bit taller
|
||||
OK(getFromSocket("/dispatch resizeactive +20 +20"));
|
||||
|
||||
// now we test focus methods.
|
||||
OK(getFromSocket("/keyword binds:focus_preferred_method 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_C"));
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_C");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
|
||||
OK(getFromSocket("/keyword binds:focus_preferred_method 1"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_B");
|
||||
}
|
||||
|
||||
NLog::log("{}Testing movefocus_cycles_fullscreen", Colors::YELLOW);
|
||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||
OK(getFromSocket("/dispatch focusmonitor HEADLESS-3"));
|
||||
Tests::spawnKitty("kitty_D");
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_D");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor l"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:movefocus_cycles_fullscreen false"));
|
||||
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_D");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/dispatch focusmonitor l"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_A");
|
||||
}
|
||||
|
||||
OK(getFromSocket("/keyword binds:movefocus_cycles_fullscreen true"));
|
||||
|
||||
OK(getFromSocket("/dispatch movefocus r"));
|
||||
|
||||
{
|
||||
auto str = getFromSocket("/activewindow");
|
||||
EXPECT_CONTAINS(str, "class: kitty_B");
|
||||
}
|
||||
|
||||
// destroy the headless output
|
||||
OK(getFromSocket("/output remove HEADLESS-3"));
|
||||
|
||||
// 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)
|
@@ -1,31 +0,0 @@
|
||||
#include "plugin.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <print>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include "../shared.hpp"
|
||||
|
||||
bool testPlugin() {
|
||||
const auto RESPONSE = getFromSocket("/dispatch plugin:test:test");
|
||||
|
||||
if (RESPONSE != "ok") {
|
||||
NLog::log("{}Plugin tests failed, plugin returned:\n{}{}", Colors::RED, Colors::RESET, RESPONSE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool testVkb() {
|
||||
const auto RESPONSE = getFromSocket("/dispatch plugin:test:vkb");
|
||||
|
||||
if (RESPONSE != "ok") {
|
||||
NLog::log("{}Vkb tests failed, tests returned:\n{}{}", Colors::RED, Colors::RESET, RESPONSE);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
bool testPlugin();
|
||||
bool testVkb();
|
@@ -1,92 +0,0 @@
|
||||
#include "shared.hpp"
|
||||
#include <csignal>
|
||||
#include <cerrno>
|
||||
#include <thread>
|
||||
#include <print>
|
||||
#include "../shared.hpp"
|
||||
#include "../hyprctlCompat.hpp"
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_) {
|
||||
const auto COUNT_BEFORE = windowCount();
|
||||
|
||||
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();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
// wait while kitty spawns
|
||||
int counter = 0;
|
||||
while (processAlive(kitty->pid()) && windowCount() == COUNT_BEFORE) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!processAlive(kitty->pid()))
|
||||
return nullptr;
|
||||
|
||||
return kitty;
|
||||
}
|
||||
|
||||
bool Tests::processAlive(pid_t pid) {
|
||||
errno = 0;
|
||||
int ret = kill(pid, 0);
|
||||
return ret != -1 || errno != ESRCH;
|
||||
}
|
||||
|
||||
int Tests::windowCount() {
|
||||
return countOccurrences(getFromSocket("/clients"), "focusHistoryID: ");
|
||||
}
|
||||
|
||||
int Tests::countOccurrences(const std::string& in, const std::string& what) {
|
||||
int cnt = 0;
|
||||
auto pos = in.find(what);
|
||||
while (pos != std::string::npos) {
|
||||
cnt++;
|
||||
pos = in.find(what, pos + what.length() - 1);
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
bool Tests::killAllWindows() {
|
||||
auto str = getFromSocket("/clients");
|
||||
auto pos = str.find("Window ");
|
||||
while (pos != std::string::npos) {
|
||||
auto pos2 = str.find(" -> ", pos);
|
||||
getFromSocket("/dispatch killwindow address:0x" + str.substr(pos + 7, pos2 - pos - 7));
|
||||
pos = str.find("Window ", pos + 5);
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != 0) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
std::println("{}Timed out waiting for windows to close", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tests::waitUntilWindowsN(int n) {
|
||||
int counter = 0;
|
||||
while (Tests::windowCount() != n) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
std::println("{}Timed out waiting for windows", Colors::RED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "../Log.hpp"
|
||||
|
||||
//NOLINTNEXTLINE
|
||||
namespace Tests {
|
||||
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);
|
||||
};
|
@@ -1,325 +0,0 @@
|
||||
# This is an example Hyprland config file.
|
||||
# Refer to the wiki for more information.
|
||||
# https://wiki.hyprland.org/Configuring/
|
||||
|
||||
# Please note not all available settings / options are set here.
|
||||
# For a full list, see the wiki
|
||||
|
||||
# You can split this configuration into multiple files
|
||||
# Create your files separately and then link them to this file like this:
|
||||
# source = ~/.config/hypr/myColors.conf
|
||||
|
||||
|
||||
################
|
||||
### MONITORS ###
|
||||
################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Monitors/
|
||||
|
||||
monitor=HEADLESS-1,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-2,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-3,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-4,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-5,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-6,1920x1080@60,auto-right,1
|
||||
monitor=HEADLESS-PERSISTENT-TEST,1920x1080@60,auto-right,1
|
||||
|
||||
monitor=,disabled
|
||||
|
||||
|
||||
###################
|
||||
### MY PROGRAMS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
|
||||
# Set programs that you use
|
||||
$terminal = kitty
|
||||
$fileManager = dolphin
|
||||
$menu = wofi --show drun
|
||||
|
||||
|
||||
#################
|
||||
### AUTOSTART ###
|
||||
#################
|
||||
|
||||
# Autostart necessary processes (like notifications daemons, status bars, etc.)
|
||||
# Or execute your favorite apps at launch like this:
|
||||
|
||||
# exec-once = $terminal
|
||||
# exec-once = nm-applet &
|
||||
# exec-once = waybar & hyprpaper & firefox
|
||||
|
||||
|
||||
#############################
|
||||
### ENVIRONMENT VARIABLES ###
|
||||
#############################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Environment-variables/
|
||||
|
||||
env = XCURSOR_SIZE,24
|
||||
env = HYPRCURSOR_SIZE,24
|
||||
|
||||
|
||||
#####################
|
||||
### LOOK AND FEEL ###
|
||||
#####################
|
||||
|
||||
# Refer to https://wiki.hyprland.org/Configuring/Variables/
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#general
|
||||
general {
|
||||
gaps_in = 5
|
||||
gaps_out = 20
|
||||
|
||||
border_size = 2
|
||||
|
||||
snap {
|
||||
enabled = true
|
||||
window_gap = 8
|
||||
monitor_gap = 10
|
||||
respect_gaps = false
|
||||
border_overlap = false
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
|
||||
col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg
|
||||
col.inactive_border = rgba(595959aa)
|
||||
|
||||
# Set to true enable resizing windows by clicking and dragging on borders and gaps
|
||||
resize_on_border = false
|
||||
|
||||
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
|
||||
allow_tearing = false
|
||||
|
||||
layout = dwindle
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#decoration
|
||||
decoration {
|
||||
rounding = 10
|
||||
rounding_power = 2
|
||||
|
||||
# Change transparency of focused and unfocused windows
|
||||
active_opacity = 1.0
|
||||
inactive_opacity = 1.0
|
||||
|
||||
shadow {
|
||||
enabled = true
|
||||
range = 4
|
||||
render_power = 3
|
||||
color = rgba(1a1a1aee)
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#blur
|
||||
blur {
|
||||
enabled = true
|
||||
size = 3
|
||||
passes = 1
|
||||
|
||||
vibrancy = 0.1696
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#animations
|
||||
animations {
|
||||
enabled = 0
|
||||
|
||||
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
|
||||
|
||||
bezier = easeOutQuint,0.23,1,0.32,1
|
||||
bezier = easeInOutCubic,0.65,0.05,0.36,1
|
||||
bezier = linear,0,0,1,1
|
||||
bezier = almostLinear,0.5,0.5,0.75,1.0
|
||||
bezier = quick,0.15,0,0.1,1
|
||||
|
||||
animation = global, 1, 10, default
|
||||
animation = border, 1, 5.39, easeOutQuint
|
||||
animation = windows, 1, 4.79, easeOutQuint
|
||||
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
|
||||
animation = windowsOut, 1, 1.49, linear, popin 87%
|
||||
animation = fadeIn, 1, 1.73, almostLinear
|
||||
animation = fadeOut, 1, 1.46, almostLinear
|
||||
animation = fade, 1, 3.03, quick
|
||||
animation = layers, 1, 3.81, easeOutQuint
|
||||
animation = layersIn, 1, 4, easeOutQuint, fade
|
||||
animation = layersOut, 1, 1.5, linear, fade
|
||||
animation = fadeLayersIn, 1, 1.79, almostLinear
|
||||
animation = fadeLayersOut, 1, 1.39, almostLinear
|
||||
animation = workspaces, 1, 1.94, almostLinear, fade
|
||||
animation = workspacesIn, 1, 1.21, almostLinear, fade
|
||||
animation = workspacesOut, 1, 1.94, almostLinear, fade
|
||||
}
|
||||
|
||||
# Ref https://wiki.hyprland.org/Configuring/Workspace-Rules/
|
||||
# "Smart gaps" / "No gaps when only"
|
||||
# uncomment all if you wish to use that.
|
||||
# workspace = w[tv1], gapsout:0, gapsin:0
|
||||
# workspace = f[1], gapsout:0, gapsin:0
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:w[tv1]
|
||||
# windowrulev2 = bordersize 0, floating:0, onworkspace:f[1]
|
||||
# windowrulev2 = rounding 0, floating:0, onworkspace:f[1]
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||
preserve_split = true # You probably want this
|
||||
split_bias = 1
|
||||
}
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
|
||||
master {
|
||||
new_status = master
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
||||
misc {
|
||||
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
||||
disable_hyprland_logo = false # If true disables the random hyprland logo / anime girl background. :(
|
||||
}
|
||||
|
||||
|
||||
#############
|
||||
### INPUT ###
|
||||
#############
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#input
|
||||
input {
|
||||
kb_layout = us
|
||||
kb_variant =
|
||||
kb_model =
|
||||
kb_options =
|
||||
kb_rules =
|
||||
|
||||
follow_mouse = 1
|
||||
|
||||
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
|
||||
|
||||
touchpad {
|
||||
natural_scroll = false
|
||||
}
|
||||
}
|
||||
|
||||
# https://wiki.hyprland.org/Configuring/Variables/#gestures
|
||||
gestures {
|
||||
|
||||
}
|
||||
|
||||
# Example per-device config
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more
|
||||
device {
|
||||
name = epic-mouse-v1
|
||||
sensitivity = -0.5
|
||||
}
|
||||
|
||||
|
||||
###################
|
||||
### KEYBINDINGS ###
|
||||
###################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Keywords/
|
||||
$mainMod = SUPER # Sets "Windows" key as main modifier
|
||||
|
||||
# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
|
||||
bind = $mainMod, Q, exec, $terminal
|
||||
bind = $mainMod, C, killactive,
|
||||
bind = $mainMod, M, exit,
|
||||
bind = $mainMod, E, exec, $fileManager
|
||||
bind = $mainMod, V, togglefloating,
|
||||
bind = $mainMod, R, exec, $menu
|
||||
bind = $mainMod, P, pseudo, # dwindle
|
||||
bind = $mainMod, J, togglesplit, # dwindle
|
||||
|
||||
# Move focus with mainMod + arrow keys
|
||||
bind = $mainMod, left, movefocus, l
|
||||
bind = $mainMod, right, movefocus, r
|
||||
bind = $mainMod, up, movefocus, u
|
||||
bind = $mainMod, down, movefocus, d
|
||||
|
||||
# Switch workspaces with mainMod + [0-9]
|
||||
bind = $mainMod, 1, workspace, 1
|
||||
bind = $mainMod, 2, workspace, 2
|
||||
bind = $mainMod, 3, workspace, 3
|
||||
bind = $mainMod, 4, workspace, 4
|
||||
bind = $mainMod, 5, workspace, 5
|
||||
bind = $mainMod, 6, workspace, 6
|
||||
bind = $mainMod, 7, workspace, 7
|
||||
bind = $mainMod, 8, workspace, 8
|
||||
bind = $mainMod, 9, workspace, 9
|
||||
bind = $mainMod, 0, workspace, 10
|
||||
|
||||
# Move active window to a workspace with mainMod + SHIFT + [0-9]
|
||||
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||
bind = $mainMod SHIFT, 2, movetoworkspace, 2
|
||||
bind = $mainMod SHIFT, 3, movetoworkspace, 3
|
||||
bind = $mainMod SHIFT, 4, movetoworkspace, 4
|
||||
bind = $mainMod SHIFT, 5, movetoworkspace, 5
|
||||
bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
||||
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
||||
|
||||
# Example special workspace (scratchpad)
|
||||
bind = $mainMod, S, togglespecialworkspace, magic
|
||||
bind = $mainMod SHIFT, S, movetoworkspace, special:magic
|
||||
|
||||
# Scroll through existing workspaces with mainMod + scroll
|
||||
bind = $mainMod, mouse_down, workspace, e+1
|
||||
bind = $mainMod, mouse_up, workspace, e-1
|
||||
|
||||
# Move/resize windows with mainMod + LMB/RMB and dragging
|
||||
bindm = $mainMod, mouse:272, movewindow
|
||||
bindm = $mainMod, mouse:273, resizewindow
|
||||
|
||||
# Laptop multimedia keys for volume and LCD brightness
|
||||
bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+
|
||||
bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
|
||||
bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
|
||||
bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
|
||||
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s 10%+
|
||||
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
|
||||
|
||||
# Requires playerctl
|
||||
bindl = , XF86AudioNext, exec, playerctl next
|
||||
bindl = , XF86AudioPause, exec, playerctl play-pause
|
||||
bindl = , XF86AudioPlay, exec, playerctl play-pause
|
||||
bindl = , XF86AudioPrev, exec, playerctl previous
|
||||
|
||||
##############################
|
||||
### WINDOWS AND WORKSPACES ###
|
||||
##############################
|
||||
|
||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||
|
||||
# Example windowrule v1
|
||||
# windowrule = float, ^(kitty)$
|
||||
|
||||
# Example windowrule v2
|
||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
||||
|
||||
# Ignore maximize requests from apps. You'll probably like this.
|
||||
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
|
||||
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
|
||||
gesture = 3, up, close
|
||||
gesture = 3, down, fullscreen
|
||||
|
||||
gesture = 3, down, mod:ALT, float
|
||||
|
||||
gesture = 3, horizontal, mod:ALT, workspace
|
||||
|
41
meson.build
41
meson.build
@@ -21,7 +21,6 @@ add_project_arguments(
|
||||
'-Wno-missing-field-initializers',
|
||||
'-Wno-narrowing',
|
||||
'-Wno-pointer-arith', datarootdir,
|
||||
'-DHYPRLAND_VERSION="' + meson.project_version() + '"',
|
||||
],
|
||||
language: 'cpp',
|
||||
)
|
||||
@@ -31,20 +30,8 @@ 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('.')
|
||||
aquamarine = dependency('aquamarine')
|
||||
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'))
|
||||
@@ -62,19 +49,14 @@ 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')
|
||||
if get_option('systemd').enabled()
|
||||
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
|
||||
subdir('systemd')
|
||||
endif
|
||||
|
||||
if get_option('legacy_renderer').enabled()
|
||||
add_project_arguments('-DLEGACY_RENDERER', language: 'cpp')
|
||||
endif
|
||||
|
||||
if get_option('buildtype') == 'debug'
|
||||
@@ -83,11 +65,9 @@ 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)
|
||||
globber = run_command('find', 'src', '-name', '*.h*', check: true)
|
||||
headers = globber.stdout().strip().split('\n')
|
||||
foreach file : headers
|
||||
install_headers(file, subdir: 'hyprland', preserve_path: true)
|
||||
@@ -99,19 +79,14 @@ 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('hyprpm/src')
|
||||
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')
|
||||
|
||||
|
@@ -1,5 +1,4 @@
|
||||
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('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer')
|
||||
option('tracy_enable', type: 'boolean', value: false , description: 'Enable profiling')
|
||||
|
@@ -5,19 +5,14 @@
|
||||
pkg-config,
|
||||
pkgconf,
|
||||
makeWrapper,
|
||||
cmake,
|
||||
meson,
|
||||
ninja,
|
||||
aquamarine,
|
||||
binutils,
|
||||
cairo,
|
||||
epoll-shim,
|
||||
git,
|
||||
glaze,
|
||||
hyprcursor,
|
||||
hyprgraphics,
|
||||
hyprland-protocols,
|
||||
hyprland-qtutils,
|
||||
hyprlang,
|
||||
hyprutils,
|
||||
hyprwayland-scanner,
|
||||
@@ -27,10 +22,9 @@
|
||||
libinput,
|
||||
libxkbcommon,
|
||||
libuuid,
|
||||
libgbm,
|
||||
mesa,
|
||||
pango,
|
||||
pciutils,
|
||||
re2,
|
||||
systemd,
|
||||
tomlplusplus,
|
||||
udis86-hyprland,
|
||||
@@ -41,6 +35,7 @@
|
||||
xwayland,
|
||||
debug ? false,
|
||||
enableXWayland ? true,
|
||||
legacyRenderer ? false,
|
||||
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
|
||||
wrapRuntimeDeps ? true,
|
||||
version ? "git",
|
||||
@@ -51,52 +46,33 @@
|
||||
enableNvidiaPatches ? false,
|
||||
nvidiaPatches ? false,
|
||||
hidpiXWayland ? false,
|
||||
legacyRenderer ? false,
|
||||
}: let
|
||||
inherit (builtins) foldl' readFile;
|
||||
inherit (builtins) baseNameOf foldl';
|
||||
inherit (lib.asserts) assertMsg;
|
||||
inherit (lib.attrsets) mapAttrsToList;
|
||||
inherit (lib.lists) flatten concatLists optional optionals;
|
||||
inherit (lib.strings) makeBinPath optionalString mesonBool mesonEnable trim;
|
||||
fs = lib.fileset;
|
||||
inherit (lib.sources) cleanSourceWith cleanSource;
|
||||
inherit (lib.strings) hasSuffix makeBinPath optionalString mesonBool mesonEnable;
|
||||
|
||||
adapters = flatten [
|
||||
stdenvAdapters.useMoldLinker
|
||||
(lib.optional debug stdenvAdapters.keepDebugInfo)
|
||||
];
|
||||
|
||||
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
||||
in
|
||||
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
|
||||
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
|
||||
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland";
|
||||
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
|
||||
customStdenv.mkDerivation (finalAttrs: {
|
||||
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hyprland.org/Configuring/XWayland";
|
||||
customStdenv.mkDerivation {
|
||||
pname = "hyprland${optionalString debug "-debug"}";
|
||||
inherit version;
|
||||
|
||||
src = fs.toSource {
|
||||
root = ../.;
|
||||
fileset =
|
||||
fs.intersection
|
||||
# allows non-flake builds to only include files tracked by git
|
||||
(fs.gitTracked ../.)
|
||||
(fs.unions [
|
||||
../assets/hyprland-portals.conf
|
||||
../assets/install
|
||||
../hyprctl
|
||||
../hyprland.pc.in
|
||||
../LICENSE
|
||||
../meson_options.txt
|
||||
../protocols
|
||||
../src
|
||||
../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 == "meson.build") ../.)
|
||||
]);
|
||||
src = cleanSourceWith {
|
||||
filter = name: type: let
|
||||
baseName = baseNameOf (toString name);
|
||||
in
|
||||
! (hasSuffix ".nix" baseName);
|
||||
src = cleanSource ../.;
|
||||
};
|
||||
|
||||
postPatch = ''
|
||||
@@ -111,7 +87,6 @@ in
|
||||
DATE = date;
|
||||
DIRTY = optionalString (commit == "") "dirty";
|
||||
HASH = commit;
|
||||
TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
|
||||
|
||||
depsBuildBuild = [
|
||||
pkg-config
|
||||
@@ -122,7 +97,6 @@ in
|
||||
makeWrapper
|
||||
meson
|
||||
ninja
|
||||
cmake # needed for glaze
|
||||
pkg-config
|
||||
];
|
||||
|
||||
@@ -137,9 +111,7 @@ in
|
||||
aquamarine
|
||||
cairo
|
||||
git
|
||||
glaze
|
||||
hyprcursor
|
||||
hyprgraphics
|
||||
hyprland-protocols
|
||||
hyprlang
|
||||
hyprutils
|
||||
@@ -148,10 +120,9 @@ in
|
||||
libinput
|
||||
libuuid
|
||||
libxkbcommon
|
||||
libgbm
|
||||
mesa
|
||||
pango
|
||||
pciutils
|
||||
re2
|
||||
tomlplusplus
|
||||
udis86-hyprland
|
||||
wayland
|
||||
@@ -159,7 +130,6 @@ in
|
||||
wayland-scanner
|
||||
xorg.libXcursor
|
||||
]
|
||||
(optionals customStdenv.hostPlatform.isBSD [epoll-shim])
|
||||
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
|
||||
(optionals enableXWayland [
|
||||
xorg.libxcb
|
||||
@@ -172,19 +142,19 @@ in
|
||||
(optional withSystemd systemd)
|
||||
];
|
||||
|
||||
strictDeps = true;
|
||||
|
||||
mesonBuildType =
|
||||
if debug
|
||||
then "debug"
|
||||
then "debugoptimized"
|
||||
else "release";
|
||||
|
||||
# we want as much debug info as possible
|
||||
dontStrip = debug;
|
||||
|
||||
mesonFlags = flatten [
|
||||
(mapAttrsToList mesonEnable {
|
||||
"xwayland" = enableXWayland;
|
||||
"legacy_renderer" = legacyRenderer;
|
||||
"systemd" = withSystemd;
|
||||
"uwsm" = false;
|
||||
"hyprpm" = false;
|
||||
})
|
||||
(mapAttrsToList mesonBool {
|
||||
"b_pch" = false;
|
||||
@@ -197,7 +167,6 @@ in
|
||||
wrapProgram $out/bin/Hyprland \
|
||||
--suffix PATH : ${makeBinPath [
|
||||
binutils
|
||||
hyprland-qtutils
|
||||
pciutils
|
||||
pkgconf
|
||||
]}
|
||||
@@ -213,4 +182,4 @@ in
|
||||
platforms = lib.platforms.linux;
|
||||
mainProgram = "Hyprland";
|
||||
};
|
||||
})
|
||||
}
|
||||
|
@@ -1,64 +0,0 @@
|
||||
{
|
||||
writeShellApplication,
|
||||
deadnix,
|
||||
statix,
|
||||
alejandra,
|
||||
llvmPackages_19,
|
||||
fd,
|
||||
}:
|
||||
writeShellApplication {
|
||||
name = "hyprland-treewide-formatter";
|
||||
runtimeInputs = [
|
||||
deadnix
|
||||
statix
|
||||
alejandra
|
||||
llvmPackages_19.clang-tools
|
||||
fd
|
||||
];
|
||||
text = ''
|
||||
# shellcheck disable=SC2148
|
||||
|
||||
# common excludes
|
||||
excludes="subprojects"
|
||||
|
||||
nix_format() {
|
||||
if [ "$*" = 0 ]; then
|
||||
fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
|
||||
fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \;
|
||||
elif [ -d "$1" ]; then
|
||||
fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
|
||||
fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \;
|
||||
else
|
||||
statix fix -- "$1"
|
||||
deadnix -e "$1"
|
||||
alejandra "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
cpp_format() {
|
||||
if [ "$*" = 0 ] || [ "$1" = "." ]; then
|
||||
fd '.*\.cpp' . -E "$excludes" | xargs clang-format --verbose -i
|
||||
elif [ -d "$1" ]; then
|
||||
fd '.*\.cpp' "$1" -E "$excludes" | xargs clang-format --verbose -i
|
||||
else
|
||||
clang-format --verbose -i "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
for i in "$@"; do
|
||||
case ''${i##*.} in
|
||||
"nix")
|
||||
nix_format "$i"
|
||||
;;
|
||||
"cpp")
|
||||
cpp_format "$i"
|
||||
;;
|
||||
*)
|
||||
nix_format "$i"
|
||||
cpp_format "$i"
|
||||
;;
|
||||
esac
|
||||
|
||||
done
|
||||
'';
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user