Compare commits
440 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b41882c169 | |||
| bcab43b181 | |||
| 08e215c9bf | |||
|
|
4152ac76d0 | ||
|
|
a5858018d8 | ||
|
|
8685fd7b0c | ||
|
|
1fa157cf6d | ||
|
|
e0c5710059 | ||
|
|
42f0a6005b | ||
|
|
ae9ca17b40 | ||
|
|
972f23efe8 | ||
|
|
4c60d9df70 | ||
|
|
b7dfb47566 | ||
|
|
3284dd729b | ||
|
|
803e81ac39 | ||
|
|
34c7cc7d38 | ||
|
|
c47ae950f4 | ||
|
|
3f169ee5de | ||
|
|
10754745a9 | ||
|
|
8271cfc97b | ||
|
|
c11cadd8d6 | ||
|
|
dc4b082ee8 | ||
|
|
edf7098345 | ||
|
|
7299a3b0d5 | ||
|
|
b06a4b5e13 | ||
|
|
3faddf40d0 | ||
|
|
a6e3a2478c | ||
|
|
ff0b706ea3 | ||
|
|
4f44df7b17 | ||
|
|
be03497b82 | ||
|
|
ff20cbf89c | ||
|
|
fe0a202137 | ||
|
|
75a815fbf2 | ||
|
|
3b7401b065 | ||
|
|
d4d17d5d52 | ||
|
|
52ece2b017 | ||
|
|
d98f7ffaf5 | ||
|
|
5f650f8ed9 | ||
|
|
9f98f7440b | ||
|
|
5cb1281035 | ||
|
|
743dffd638 | ||
|
|
5c370c3333 | ||
|
|
cf0d256c13 | ||
|
|
6ebafcf107 | ||
|
|
8ad96a95d6 | ||
|
|
f41e3c2203 | ||
|
|
f0a80ce5e0 | ||
|
|
2928d6af0a | ||
|
|
93aacfc0dc | ||
|
|
19c263e53c | ||
|
|
a032090098 | ||
|
|
0b55c55f4a | ||
|
|
85c2764f5e | ||
|
|
c2bed4103c | ||
|
|
82729db330 | ||
|
|
f12904e641 | ||
|
|
b90c61c04f | ||
|
|
e333a330c0 | ||
|
|
1c64ef06d9 | ||
|
|
6b2c08d3e8 | ||
|
|
db8509dfe2 | ||
|
|
d2b9957fab | ||
|
|
362ea7b0f3 | ||
|
|
f7114016c6 | ||
|
|
0002f148c9 | ||
|
|
ffec41c426 | ||
|
|
f624449c12 | ||
|
|
70cdd819e4 | ||
|
|
cc14dd1baf | ||
|
|
c71fbd854d | ||
|
|
0e9196867b | ||
|
|
1e06ab464f | ||
|
|
623185170b | ||
|
|
d0583e1761 | ||
|
|
5b2efe54b1 | ||
|
|
457617b5a3 | ||
|
|
c60b3cb2ed | ||
|
|
fbf67ef050 | ||
|
|
be893a81b4 | ||
|
|
5a80bc120a | ||
|
|
a248805132 | ||
|
|
8ab4d1dc06 | ||
|
|
ae82a55400 | ||
|
|
bc09504ea5 | ||
|
|
f4bc8c3a64 | ||
|
|
b88813c7ef | ||
|
|
b4ee4674f9 | ||
|
|
93dbf88426 | ||
|
|
0eb4755a3e | ||
|
|
723870337f | ||
|
|
51f8849e54 | ||
|
|
b9b1eda2ef | ||
|
|
13dab66b1d | ||
|
|
9f59ed7868 | ||
|
|
a20142bcce | ||
|
|
d91952c555 | ||
|
|
8b17a7404b | ||
|
|
9ea6d0e15f | ||
|
|
a1e62dcb12 | ||
|
|
7a566942d5 | ||
|
|
68456a5d9a | ||
|
|
184af52f24 | ||
|
|
1af260ecbe | ||
|
|
0de216e783 | ||
|
|
661314e134 | ||
|
|
17fc159ae2 | ||
|
|
6716b8a0e3 | ||
|
|
59f19e465b | ||
|
|
e6ca141364 | ||
|
|
48176160ab | ||
|
|
e5a2b9e5b0 | ||
|
|
e80f705d76 | ||
|
|
1bf410e1fc | ||
|
|
1c767de9da | ||
|
|
a8a8929bb4 | ||
|
|
eb0d3f9f01 | ||
|
|
81a029e504 | ||
|
|
380d14998e | ||
|
|
fd48d102e1 | ||
|
|
531fc43203 | ||
|
|
5b6c42ca70 | ||
|
|
857a78ce4e | ||
|
|
339661229d | ||
|
|
ff061d177e | ||
|
|
407a623801 | ||
|
|
171ad7d338 | ||
|
|
f16ebef003 | ||
|
|
6507445787 | ||
|
|
f68ac7ef75 | ||
|
|
60f1c61323 | ||
|
|
9f9dbb0dc5 | ||
|
|
cfbbfb591a | ||
|
|
63eb6b3bda | ||
|
|
8606bc255b | ||
|
|
562171ab66 | ||
|
|
9ce9ef2705 | ||
|
|
02ff413002 | ||
|
|
1bc857b12c | ||
|
|
cd7bdc7a43 | ||
|
|
e123fd3e66 | ||
|
|
30756d8718 | ||
|
|
9433060760 | ||
|
|
a0ec2e4daf | ||
|
|
d9d9d9358f | ||
|
|
95c8f8b299 | ||
|
|
beeca9dacb | ||
|
|
47f9035601 | ||
|
|
db6114c6c5 | ||
|
|
cbeb6984e7 | ||
|
|
4330b49a84 | ||
|
|
2ad7f6edd4 | ||
|
|
ec120d5732 | ||
|
|
fe6c213024 | ||
|
|
b8fc0def97 | ||
|
|
c92fb5e85f | ||
|
|
7d209b2941 | ||
|
|
e92b20292b | ||
|
|
22e53345ba | ||
|
|
116537b494 | ||
|
|
c8b5023bb0 | ||
|
|
bcb34275ea | ||
|
|
21325f9385 | ||
|
|
50454c6d17 | ||
|
|
c65c7614bc | ||
|
|
891e029ba3 | ||
|
|
b1d1c9843f | ||
|
|
2a2c2b0e28 | ||
|
|
64db62d7e2 | ||
|
|
82de66a030 | ||
|
|
22fc8136a2 | ||
|
|
e7985ca4c4 | ||
|
|
6c3ebed76e | ||
|
|
f9fb24577a | ||
|
|
55f40ecc95 | ||
|
|
441a8714c7 | ||
|
|
57e6a57e6b | ||
|
|
c44292c723 | ||
|
|
f0b6714539 | ||
|
|
8f547c6fa0 | ||
|
|
d6e2ae0247 | ||
|
|
eb0480ba0d | ||
|
|
0896775f1b | ||
|
|
c99eb23869 | ||
|
|
92a3b91999 | ||
|
|
36aa465a21 | ||
|
|
fec17e5e79 | ||
|
|
eff484b96c | ||
|
|
2e697ce2bf | ||
|
|
0b13d398fe | ||
|
|
ac9df44788 | ||
|
|
e0cf88809d | ||
|
|
e43f949f8a | ||
|
|
8d03fcc8d7 | ||
|
|
5e18111121 | ||
|
|
fbf421df88 | ||
|
|
8f8b31e7a6 | ||
|
|
81e7498ec2 | ||
|
|
fa41c8229d | ||
|
|
5b1b79c29c | ||
|
|
eb623bd91d | ||
|
|
3dcaadbdf5 | ||
|
|
a649dbe4c4 | ||
|
|
f767782e3f | ||
|
|
f54dd4da4a | ||
|
|
3aa4e02720 | ||
|
|
8368566044 | ||
|
|
918e2bb9be | ||
|
|
a383ca1866 | ||
|
|
f1652b2951 | ||
|
|
cbfbd9712a | ||
|
|
9817553c66 | ||
|
|
6fce2d7288 | ||
|
|
107275238c | ||
|
|
3b77c784e2 | ||
|
|
d46df728fd | ||
|
|
8eb3ecc755 | ||
|
|
97c8a2f1cf | ||
|
|
a492fa3866 | ||
|
|
e165f84184 | ||
|
|
686eda9d48 | ||
|
|
70c5fe5cd8 | ||
|
|
32978176b1 | ||
|
|
1761909bca | ||
|
|
7d8f57083e | ||
|
|
a3c8533d74 | ||
|
|
0b3b012817 | ||
|
|
583c4074a5 | ||
|
|
922e53c68c | ||
|
|
17bc3b83db | ||
|
|
fab3370254 | ||
|
|
ee67278038 | ||
|
|
b9bd9d147f | ||
|
|
ec4beb1b39 | ||
|
|
31d3181e1e | ||
|
|
9b93d621b1 | ||
|
|
bd7f9aad05 | ||
|
|
48a024e032 | ||
|
|
bd02178e96 | ||
|
|
214fdb099c | ||
|
|
d622c09d09 | ||
|
|
529559712b | ||
|
|
293d3e5de9 | ||
|
|
f8464866eb | ||
|
|
ea444c35bb | ||
|
|
6a055fc747 | ||
|
|
a8452705d6 | ||
|
|
e5d20b56bc | ||
|
|
5faa66d297 | ||
|
|
610c59dc34 | ||
|
|
e5f22c06b4 | ||
|
|
6d3b17ee83 | ||
|
|
42447a50d6 | ||
|
|
d7f26038ee | ||
|
|
33df518f97 | ||
|
|
9ea565054a | ||
|
|
1f1a39d46c | ||
|
|
14c49230cc | ||
|
|
2525052779 | ||
|
|
f7f357f15f | ||
|
|
abffe75088 | ||
|
|
60efbf3f63 | ||
|
|
712bcfbce5 | ||
|
|
c87a1a7629 | ||
|
|
7bd207377c | ||
|
|
b9bef69554 | ||
|
|
70f54a1e1b | ||
|
|
f6c5c659a7 | ||
|
|
c23a0c20a4 | ||
|
|
3bbbb5aaca | ||
|
|
315806f598 | ||
|
|
6175ecd4c4 | ||
|
|
f88deb928a | ||
|
|
18901b8e59 | ||
|
|
7098558420 | ||
|
|
59438908de | ||
|
|
cbfdbe9fa1 | ||
|
|
c94a981711 | ||
|
|
beb1b578e8 | ||
|
|
c5beecb2c3 | ||
|
|
6e09eb2e6c | ||
|
|
6b491e4d6b | ||
|
|
4036c37e55 | ||
|
|
7ccc57eb7c | ||
|
|
e4a8f2b14f | ||
|
|
6535ff07c9 | ||
|
|
05ccbb2f2d | ||
|
|
09e195d1f2 | ||
|
|
fd5e790d08 | ||
|
|
69db0bcae6 | ||
|
|
8dfdcfb353 | ||
|
|
5700736505 | ||
|
|
75f6435f70 | ||
|
|
5dd224805d | ||
|
|
2ca7ad7efc | ||
|
|
9aa313402b | ||
|
|
1ff801f5f3 | ||
|
|
3cf6dfd7e6 | ||
|
|
f58c80fd39 | ||
|
|
6712fb954f | ||
|
|
efe665b455 | ||
|
|
920353370b | ||
|
|
834f019bab | ||
|
|
a5b7c91329 | ||
|
|
916e5d1aea | ||
|
|
9584b2d40e | ||
|
|
532ca053d6 | ||
|
|
ca99e8228c | ||
|
|
8ca40479a7 | ||
|
|
c26e91f074 | ||
|
|
76ac655c9e | ||
|
|
f8d5aad1a1 | ||
|
|
b8bb5e9bde | ||
|
|
7797deb935 | ||
|
|
d3c9c54b79 | ||
|
|
cedadf4fdc | ||
|
|
222dbe99d0 | ||
|
|
ebe74be75a | ||
|
|
afeda6cee6 | ||
|
|
6a1daff5f3 | ||
|
|
016eb7a23d | ||
|
|
ec6756f961 | ||
|
|
9264436f35 | ||
|
|
d5c52ef58e | ||
|
|
52b3c8cbc6 | ||
|
|
279a07c2ce | ||
|
|
17ae3fb704 | ||
|
|
43ed0db3b3 | ||
|
|
38f912c401 | ||
|
|
9cd070fd31 | ||
|
|
d9657a95cb | ||
|
|
9b1891e476 | ||
|
|
93e5e92b0a | ||
|
|
3cf0280b11 | ||
|
|
2cadc8abab | ||
|
|
f82a8630d7 | ||
|
|
bb963fb002 | ||
|
|
f11cf6f1de | ||
|
|
574ee71d56 | ||
|
|
7e1e24fea6 | ||
|
|
68eecf61cd | ||
|
|
f9742ab501 | ||
|
|
e42185b83d | ||
|
|
a51918fd27 | ||
|
|
379ee99c68 | ||
|
|
4036e35e73 | ||
|
|
d21f2e5729 | ||
|
|
4e5a284bc4 | ||
|
|
210930bef9 | ||
|
|
40d8fa8491 | ||
|
|
1c1746de61 | ||
|
|
619e9d285b | ||
|
|
ec3b3403e7 | ||
|
|
703394affb | ||
|
|
fe6a855bbb | ||
|
|
475e87b351 | ||
|
|
3d7ea9c02f | ||
|
|
2b0fd417d3 | ||
|
|
56904edbd2 | ||
|
|
e584a8bade | ||
|
|
2ac9ded2ac | ||
|
|
abb2f7ee6f | ||
|
|
79a2781923 | ||
|
|
d66c9222b0 | ||
|
|
b5a2ef77b7 | ||
|
|
c5d45b7653 | ||
|
|
c249a9f4b8 | ||
|
|
00cce1c8ff | ||
|
|
6b8e3358d6 | ||
|
|
80b96a3166 | ||
|
|
f9d1da6667 | ||
|
|
7532115318 | ||
|
|
1c29d6b1ba | ||
|
|
d0503bea43 | ||
|
|
fbb31503f1 | ||
|
|
9f02dca8de | ||
|
|
6a8d306992 | ||
|
|
e4b40abce6 | ||
|
|
9495f989b4 | ||
|
|
2c9c4d0905 | ||
|
|
e15409bbeb | ||
|
|
312073ce79 | ||
|
|
edc311544a | ||
|
|
37fe7b2efd | ||
|
|
c2670e9ab9 | ||
|
|
95ee08b340 | ||
|
|
ff6d771be0 | ||
|
|
64e4e913e1 | ||
|
|
ad52ba9c13 | ||
|
|
526aa1d020 | ||
|
|
5f0575737f | ||
|
|
4695f85829 | ||
|
|
1796dbcdc3 | ||
|
|
e354066945 | ||
|
|
2b14f27ca8 | ||
|
|
68c23fbdaf | ||
|
|
484d87d469 | ||
|
|
cefa63c2af | ||
|
|
9d67511871 | ||
|
|
5265fa3be8 | ||
|
|
dfb4dcd55c | ||
|
|
9d02fe9c23 | ||
|
|
76edcfc66c | ||
|
|
11451d68b7 | ||
|
|
3534dbdb89 | ||
|
|
6e2fe103bc | ||
|
|
7910bc42af | ||
|
|
0770494ddf | ||
|
|
49c0c97c5a | ||
|
|
e948445f6e | ||
|
|
7a6177532b | ||
|
|
f0de61ca21 | ||
|
|
c02a6184d3 | ||
|
|
15b4b1dd91 | ||
|
|
a6b877fec2 | ||
|
|
d2d1613e4f | ||
|
|
c7e14ecd30 | ||
|
|
9321f52e07 | ||
|
|
b04e8e00b0 | ||
|
|
5b373ea9f5 | ||
|
|
d52639fdfa | ||
|
|
e616e595ae | ||
|
|
cb47eb1d11 | ||
|
|
9b006b2c85 | ||
|
|
b35f78431f | ||
|
|
b62ab4b578 | ||
|
|
43527d3634 | ||
|
|
55a93b8a52 | ||
|
|
64ee8f8a72 | ||
|
|
b77cbad502 | ||
|
|
308226a4fc | ||
|
|
ee2168c665 | ||
|
|
c330d4334f | ||
|
|
cadf922417 | ||
|
|
ac8edc6a80 | ||
|
|
b2ea6b010c | ||
|
|
0b1d690676 | ||
|
|
2931184921 | ||
|
|
0bd11d5eb9 | ||
|
|
06b37c3907 | ||
|
|
522edc8712 |
523 changed files with 37366 additions and 23708 deletions
11
.github/actions/setup_base/action.yml
vendored
11
.github/actions/setup_base/action.yml
vendored
|
|
@ -24,6 +24,7 @@ runs:
|
||||||
glm \
|
glm \
|
||||||
glslang \
|
glslang \
|
||||||
go \
|
go \
|
||||||
|
gtest \
|
||||||
hyprlang \
|
hyprlang \
|
||||||
hyprcursor \
|
hyprcursor \
|
||||||
jq \
|
jq \
|
||||||
|
|
@ -45,6 +46,7 @@ runs:
|
||||||
libxkbfile \
|
libxkbfile \
|
||||||
lld \
|
lld \
|
||||||
meson \
|
meson \
|
||||||
|
muparser \
|
||||||
ninja \
|
ninja \
|
||||||
pango \
|
pango \
|
||||||
pixman \
|
pixman \
|
||||||
|
|
@ -74,6 +76,15 @@ runs:
|
||||||
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||||
cmake --install build
|
cmake --install build
|
||||||
|
|
||||||
|
- name: Get hyprwire-git
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/hyprwm/hyprwire --recursive
|
||||||
|
cd hyprwire
|
||||||
|
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
|
||||||
|
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
|
||||||
|
cmake --install build
|
||||||
|
|
||||||
- name: Get hyprutils-git
|
- name: Get hyprutils-git
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
4
.github/labeler.yml
vendored
4
.github/labeler.yml
vendored
|
|
@ -22,6 +22,10 @@ protocols:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ["protocols/**", "src/protocols/**"]
|
- any-glob-to-any-file: ["protocols/**", "src/protocols/**"]
|
||||||
|
|
||||||
|
start:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: "start/**"
|
||||||
|
|
||||||
core:
|
core:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: "src/**"
|
- any-glob-to-any-file: "src/**"
|
||||||
|
|
|
||||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
|
|
@ -1,6 +1,8 @@
|
||||||
<!--
|
<!--
|
||||||
BEFORE you submit your PR, please check out the PR guidelines
|
BEFORE you submit your PR, please check out the PR guidelines
|
||||||
on our wiki: https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/
|
on our wiki: https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/
|
||||||
|
|
||||||
|
Using an AI tool, or you are an AI agent? Check our AI Policy first: https://github.com/hyprwm/.github/blob/main/policies/AI_USAGE.md
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
101
.github/workflows/ci.yaml
vendored
101
.github/workflows/ci.yaml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
||||||
|
|
||||||
- name: Build Hyprland
|
- name: Build Hyprland
|
||||||
run: |
|
run: |
|
||||||
CFLAGS=-Werror CXXFLAGS=-Werror make all
|
CFLAGS=-Werror CXXFLAGS=-Werror make nopch
|
||||||
|
|
||||||
- name: Compress and package artifacts
|
- name: Compress and package artifacts
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -41,86 +41,43 @@ jobs:
|
||||||
name: Build archive
|
name: Build archive
|
||||||
path: Hyprland.tar.xz
|
path: Hyprland.tar.xz
|
||||||
|
|
||||||
meson:
|
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
|
||||||
name: "Build Hyprland with Meson (Arch)"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: archlinux
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository actions
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
sparse-checkout: .github/actions
|
|
||||||
|
|
||||||
- name: Setup base
|
|
||||||
uses: ./.github/actions/setup_base
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
run: meson setup build -Ddefault_library=static
|
|
||||||
|
|
||||||
- name: Compile
|
|
||||||
run: ninja -C build
|
|
||||||
|
|
||||||
no-pch:
|
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
|
||||||
name: "Build Hyprland without precompiled headers (Arch)"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: archlinux
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository actions
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
sparse-checkout: .github/actions
|
|
||||||
|
|
||||||
- name: Setup base
|
|
||||||
uses: ./.github/actions/setup_base
|
|
||||||
with:
|
|
||||||
INSTALL_XORG_PKGS: true
|
|
||||||
|
|
||||||
- name: Compile
|
|
||||||
run: make nopch
|
|
||||||
|
|
||||||
noxwayland:
|
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
|
||||||
name: "Build Hyprland in pure Wayland (Arch)"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: archlinux
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository actions
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
sparse-checkout: .github/actions
|
|
||||||
|
|
||||||
- name: Setup base
|
|
||||||
uses: ./.github/actions/setup_base
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
run: mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DNO_XWAYLAND:STRING=true -H./ -B./build -G Ninja
|
|
||||||
|
|
||||||
- name: Compile
|
|
||||||
run: make release
|
|
||||||
|
|
||||||
clang-format:
|
clang-format:
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||||
name: "Code Style (Arch)"
|
name: "Code Style"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: archlinux
|
image: archlinux
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository actions
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
sparse-checkout: .github/actions
|
|
||||||
|
|
||||||
- name: Setup base
|
# - name: clang-format check
|
||||||
uses: ./.github/actions/setup_base
|
# uses: jidicula/clang-format-action@v4.16.0
|
||||||
|
# with:
|
||||||
|
# exclude-regex: ^subprojects$
|
||||||
|
|
||||||
- name: Configure
|
- name: Install clang-format
|
||||||
run: meson setup build -Ddefault_library=static
|
run: |
|
||||||
|
pacman --noconfirm --noprogressbar -Syyu
|
||||||
|
pacman --noconfirm --noprogressbar -Sy clang
|
||||||
|
|
||||||
- name: clang-format check
|
- name: clang-format check
|
||||||
run: ninja -C build clang-format-check
|
run: .github/workflows/clang-format-check.sh "." "llvm" "^subprojects$" ""
|
||||||
|
|
||||||
|
- name: Save PR head commit SHA
|
||||||
|
if: failure() && github.event_name == 'pull_request'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
SHA="${{ github.event.pull_request.head.sha }}"
|
||||||
|
echo "SHA=$SHA" >> $GITHUB_ENV
|
||||||
|
- name: Save latest commit SHA if not PR
|
||||||
|
if: failure() && github.event_name != 'pull_request'
|
||||||
|
shell: bash
|
||||||
|
run: echo "SHA=${{ github.sha }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Report failure in job summary
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
DEEPLINK="${{ github.server_url }}/${{ github.repository }}/commit/${{ env.SHA }}"
|
||||||
|
echo -e "Format check failed on commit [${GITHUB_SHA:0:8}]($DEEPLINK) with files:\n$(<$GITHUB_WORKSPACE/failing-files.txt)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
|
||||||
92
.github/workflows/clang-format-check.sh
vendored
Executable file
92
.github/workflows/clang-format-check.sh
vendored
Executable file
|
|
@ -0,0 +1,92 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Adapted from https://github.com/jidicula/clang-format-action
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# check.sh #
|
||||||
|
###############################################################################
|
||||||
|
# USAGE: ./entrypoint.sh [<path>] [<fallback style>]
|
||||||
|
#
|
||||||
|
# Checks all C/C++/Protobuf/CUDA files (.h, .H, .hpp, .hh, .h++, .hxx and .c,
|
||||||
|
# .C, .cpp, .cc, .c++, .cxx, .proto, .cu) in the provided GitHub repository path
|
||||||
|
# (arg1) for conforming to clang-format. If no path is provided or provided path
|
||||||
|
# is not a directory, all C/C++/Protobuf/CUDA files are checked. If any files
|
||||||
|
# are incorrectly formatted, the script lists them and exits with 1.
|
||||||
|
#
|
||||||
|
# Define your own formatting rules in a .clang-format file at your repository
|
||||||
|
# root. Otherwise, the provided style guide (arg2) is used as a fallback.
|
||||||
|
|
||||||
|
# format_diff function
|
||||||
|
# Accepts a filepath argument. The filepath passed to this function must point
|
||||||
|
# to a C/C++/Protobuf/CUDA file.
|
||||||
|
format_diff() {
|
||||||
|
local filepath="$1"
|
||||||
|
|
||||||
|
# Invoke clang-format with dry run and formatting error output
|
||||||
|
local_format="$(clang-format \
|
||||||
|
--dry-run \
|
||||||
|
--Werror \
|
||||||
|
--style=file \
|
||||||
|
--fallback-style="$FALLBACK_STYLE" \
|
||||||
|
"${filepath}")"
|
||||||
|
|
||||||
|
local format_status="$?"
|
||||||
|
if [[ ${format_status} -ne 0 ]]; then
|
||||||
|
# Append Markdown-bulleted monospaced filepath of failing file to
|
||||||
|
# summary file.
|
||||||
|
echo "* \`$filepath\`" >>failing-files.txt
|
||||||
|
|
||||||
|
echo "Failed on file: $filepath" >&2
|
||||||
|
echo "$local_format" >&2
|
||||||
|
exit_code=1 # flip the global exit code
|
||||||
|
return "${format_status}"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_PATH="$1"
|
||||||
|
FALLBACK_STYLE="$2"
|
||||||
|
EXCLUDE_REGEX="$3"
|
||||||
|
INCLUDE_REGEX="$4"
|
||||||
|
|
||||||
|
# Set the regex to an empty string regex if nothing was provided
|
||||||
|
if [[ -z $EXCLUDE_REGEX ]]; then
|
||||||
|
EXCLUDE_REGEX="^$"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the filetype regex if nothing was provided.
|
||||||
|
# Find all C/C++/Protobuf/CUDA files:
|
||||||
|
# h, H, hpp, hh, h++, hxx
|
||||||
|
# c, C, cpp, cc, c++, cxx
|
||||||
|
# ino, pde
|
||||||
|
# proto
|
||||||
|
# cu
|
||||||
|
if [[ -z $INCLUDE_REGEX ]]; then
|
||||||
|
INCLUDE_REGEX='^.*\.((((c|C)(c|pp|xx|\+\+)?$)|((h|H)h?(pp|xx|\+\+)?$))|(ino|pde|proto|cu))$'
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$GITHUB_WORKSPACE" || exit 2
|
||||||
|
|
||||||
|
if [[ ! -d $CHECK_PATH ]]; then
|
||||||
|
echo "Not a directory in the workspace, fallback to all files." >&2
|
||||||
|
CHECK_PATH="."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# initialize exit code
|
||||||
|
exit_code=0
|
||||||
|
|
||||||
|
# All files improperly formatted will be printed to the output.
|
||||||
|
src_files=$(find "$CHECK_PATH" -name .git -prune -o -regextype posix-egrep -regex "$INCLUDE_REGEX" -print)
|
||||||
|
|
||||||
|
# check formatting in each source file
|
||||||
|
IFS=$'\n' # Loop below should separate on new lines, not spaces.
|
||||||
|
for file in $src_files; do
|
||||||
|
# Only check formatting if the path doesn't match the regex
|
||||||
|
if ! [[ ${file} =~ $EXCLUDE_REGEX ]]; then
|
||||||
|
format_diff "${file}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# global exit code is flipped to nonzero if any invocation of `format_diff` has
|
||||||
|
# a formatting difference.
|
||||||
|
exit "$exit_code"
|
||||||
36
.github/workflows/clang-format.yml
vendored
36
.github/workflows/clang-format.yml
vendored
|
|
@ -4,43 +4,23 @@ jobs:
|
||||||
clang-format:
|
clang-format:
|
||||||
permissions: write-all
|
permissions: write-all
|
||||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork
|
||||||
name: "Code Style (Arch)"
|
name: "Code Style"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
|
||||||
image: archlinux
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository actions
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
sparse-checkout: .github/actions
|
|
||||||
|
|
||||||
- name: Setup base
|
|
||||||
uses: ./.github/actions/setup_base
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
run: meson setup build -Ddefault_library=static
|
|
||||||
|
|
||||||
- name: clang-format check
|
- name: clang-format check
|
||||||
run: ninja -C build clang-format-check
|
uses: jidicula/clang-format-action@v4.16.0
|
||||||
|
with:
|
||||||
|
exclude-regex: ^subprojects$
|
||||||
|
|
||||||
- name: clang-format apply
|
- name: Create comment
|
||||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
|
||||||
run: ninja -C build clang-format
|
|
||||||
|
|
||||||
- name: Create patch
|
|
||||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||||
run: |
|
run: |
|
||||||
echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style), or directly apply this patch:' > clang-format.patch
|
echo 'Please fix the formatting issues by running [`clang-format`](https://wiki.hyprland.org/Contributing-and-Debugging/PR-Guidelines/#code-style).' > clang-format.patch
|
||||||
echo '<details>' >> clang-format.patch
|
|
||||||
echo '<summary>clang-format.patch</summary>' >> clang-format.patch
|
|
||||||
echo >> clang-format.patch
|
|
||||||
echo '```diff' >> clang-format.patch
|
|
||||||
git diff >> clang-format.patch
|
|
||||||
echo '```' >> clang-format.patch
|
|
||||||
echo >> clang-format.patch
|
|
||||||
echo '</details>' >> clang-format.patch
|
|
||||||
|
|
||||||
- name: Comment patch
|
- name: Post comment
|
||||||
if: ${{ failure() && github.event_name == 'pull_request' }}
|
if: ${{ failure() && github.event_name == 'pull_request' }}
|
||||||
uses: mshick/add-pr-comment@v2
|
uses: mshick/add-pr-comment@v2
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
45
.github/workflows/new-pr-comment.yml
vendored
Normal file
45
.github/workflows/new-pr-comment.yml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
name: "New MR welcome comment"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
comment:
|
||||||
|
if: >
|
||||||
|
github.event.pull_request.user.login != 'vaxerski' &&
|
||||||
|
github.event.pull_request.user.login != 'fufexan' &&
|
||||||
|
github.event.pull_request.user.login != 'gulafaran' &&
|
||||||
|
github.event.pull_request.user.login != 'ujint34' &&
|
||||||
|
github.event.pull_request.user.login != 'paideiadilemma' &&
|
||||||
|
github.event.pull_request.user.login != 'notashelf'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
PR_COMMENT: |
|
||||||
|
Hello and thank you for making a PR to Hyprland!
|
||||||
|
|
||||||
|
Please check the [PR Guidelines](https://wiki.hypr.land/Contributing-and-Debugging/PR-Guidelines/) and make sure your PR follows them.
|
||||||
|
It will make the entire review process faster. :)
|
||||||
|
|
||||||
|
If your code can be tested, please always add tests. See more [here](https://wiki.hypr.land/Contributing-and-Debugging/Tests/).
|
||||||
|
|
||||||
|
_beep boop, I'm just a bot. A real human will review your PR soon._
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Add comment to PR
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const pr = context.payload.pull_request;
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: pr.number,
|
||||||
|
body: process.env.PR_COMMENT,
|
||||||
|
});
|
||||||
1
.github/workflows/nix-ci.yml
vendored
1
.github/workflows/nix-ci.yml
vendored
|
|
@ -25,6 +25,5 @@ jobs:
|
||||||
|
|
||||||
test:
|
test:
|
||||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
|
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
|
||||||
needs: hyprland
|
|
||||||
uses: ./.github/workflows/nix-test.yml
|
uses: ./.github/workflows/nix-test.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|
|
||||||
16
.github/workflows/nix-test.yml
vendored
16
.github/workflows/nix-test.yml
vendored
|
|
@ -20,25 +20,13 @@ jobs:
|
||||||
- name: Restore and save Nix store
|
- name: Restore and save Nix store
|
||||||
uses: nix-community/cache-nix-action@v6
|
uses: nix-community/cache-nix-action@v6
|
||||||
with:
|
with:
|
||||||
# restore and save a cache using this key
|
# restore and save a cache using this key (per job)
|
||||||
primary-key: nix-${{ runner.os }}
|
primary-key: nix-${{ runner.os }}-${{ github.job }}
|
||||||
# if there's no cache hit, restore a cache by this prefix
|
# if there's no cache hit, restore a cache by this prefix
|
||||||
restore-prefixes-first-match: nix-${{ runner.os }}
|
restore-prefixes-first-match: nix-${{ runner.os }}
|
||||||
# collect garbage until the Nix store size (in bytes) is at most this number
|
# collect garbage until the Nix store size (in bytes) is at most this number
|
||||||
# before trying to save a new cache
|
# before trying to save a new cache
|
||||||
# 1G = 1073741824
|
|
||||||
gc-max-store-size-linux: 5G
|
gc-max-store-size-linux: 5G
|
||||||
# do purge caches
|
|
||||||
purge: true
|
|
||||||
# purge all versions of the cache
|
|
||||||
purge-prefixes: nix-${{ runner.os }}
|
|
||||||
# created more than this number of seconds ago
|
|
||||||
purge-created: 0
|
|
||||||
# or, last accessed more than this number of seconds ago
|
|
||||||
# relative to the start of the `Post Restore and save Nix store` phase
|
|
||||||
purge-last-accessed: 0
|
|
||||||
# except any version with the key that is the same as the `primary-key`
|
|
||||||
purge-primary-key: never
|
|
||||||
|
|
||||||
- uses: cachix/cachix-action@v15
|
- uses: cachix/cachix-action@v15
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
20
.github/workflows/nix-update-inputs.yml
vendored
20
.github/workflows/nix-update-inputs.yml
vendored
|
|
@ -27,25 +27,13 @@ jobs:
|
||||||
- name: Restore and save Nix store
|
- name: Restore and save Nix store
|
||||||
uses: nix-community/cache-nix-action@v6
|
uses: nix-community/cache-nix-action@v6
|
||||||
with:
|
with:
|
||||||
# restore and save a cache using this key
|
# restore and save a cache using this key (per job)
|
||||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
|
primary-key: nix-${{ runner.os }}-${{ github.job }}
|
||||||
# if there's no cache hit, restore a cache by this prefix
|
# if there's no cache hit, restore a cache by this prefix
|
||||||
restore-prefixes-first-match: nix-${{ runner.os }}-
|
restore-prefixes-first-match: nix-${{ runner.os }}
|
||||||
# collect garbage until the Nix store size (in bytes) is at most this number
|
# collect garbage until the Nix store size (in bytes) is at most this number
|
||||||
# before trying to save a new cache
|
# before trying to save a new cache
|
||||||
# 1G = 1073741824
|
gc-max-store-size-linux: 5G
|
||||||
gc-max-store-size-linux: 1G
|
|
||||||
# do purge caches
|
|
||||||
purge: true
|
|
||||||
# purge all versions of the cache
|
|
||||||
purge-prefixes: nix-${{ runner.os }}-
|
|
||||||
# created more than this number of seconds ago
|
|
||||||
purge-created: 0
|
|
||||||
# or, last accessed more than this number of seconds ago
|
|
||||||
# relative to the start of the `Post Restore and save Nix store` phase
|
|
||||||
purge-last-accessed: 0
|
|
||||||
# except any version with the key that is the same as the `primary-key`
|
|
||||||
purge-primary-key: never
|
|
||||||
|
|
||||||
- name: Update inputs
|
- name: Update inputs
|
||||||
run: nix/update-inputs.sh
|
run: nix/update-inputs.sh
|
||||||
|
|
|
||||||
16
.github/workflows/nix.yml
vendored
16
.github/workflows/nix.yml
vendored
|
|
@ -25,25 +25,13 @@ jobs:
|
||||||
- name: Restore and save Nix store
|
- name: Restore and save Nix store
|
||||||
uses: nix-community/cache-nix-action@v6
|
uses: nix-community/cache-nix-action@v6
|
||||||
with:
|
with:
|
||||||
# restore and save a cache using this key
|
# restore and save a cache using this key (per job)
|
||||||
primary-key: nix-${{ runner.os }}
|
primary-key: nix-${{ runner.os }}-${{ github.job }}
|
||||||
# if there's no cache hit, restore a cache by this prefix
|
# if there's no cache hit, restore a cache by this prefix
|
||||||
restore-prefixes-first-match: nix-${{ runner.os }}
|
restore-prefixes-first-match: nix-${{ runner.os }}
|
||||||
# collect garbage until the Nix store size (in bytes) is at most this number
|
# collect garbage until the Nix store size (in bytes) is at most this number
|
||||||
# before trying to save a new cache
|
# before trying to save a new cache
|
||||||
# 1G = 1073741824
|
|
||||||
gc-max-store-size-linux: 5G
|
gc-max-store-size-linux: 5G
|
||||||
# do purge caches
|
|
||||||
purge: true
|
|
||||||
# purge all versions of the cache
|
|
||||||
purge-prefixes: nix-${{ runner.os }}
|
|
||||||
# created more than this number of seconds ago
|
|
||||||
purge-created: 0
|
|
||||||
# or, last accessed more than this number of seconds ago
|
|
||||||
# relative to the start of the `Post Restore and save Nix store` phase
|
|
||||||
purge-last-accessed: 0
|
|
||||||
# except any version with the key that is the same as the `primary-key`
|
|
||||||
purge-primary-key: never
|
|
||||||
|
|
||||||
- uses: cachix/cachix-action@v15
|
- uses: cachix/cachix-action@v15
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
37
.github/workflows/release.yaml
vendored
37
.github/workflows/release.yaml
vendored
|
|
@ -8,20 +8,37 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
source-tarball:
|
source-tarball:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
|
||||||
image: archlinux
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository actions
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
sparse-checkout: .github/actions
|
fetch-depth: 0
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
- name: Setup base
|
- name: Populate git info in version.h.in
|
||||||
uses: ./.github/actions/setup_base
|
|
||||||
|
|
||||||
- name: Generate version
|
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B /tmp/build
|
git fetch --tags --unshallow || true
|
||||||
|
|
||||||
|
COMMIT_HASH=$(git rev-parse HEAD)
|
||||||
|
BRANCH="${GITHUB_REF_NAME:-$(git rev-parse --abbrev-ref HEAD)}"
|
||||||
|
COMMIT_MSG=$(git show -s --format=%s | sed 's/[&/]/\\&/g')
|
||||||
|
COMMIT_DATE=$(git show -s --format=%cd --date=local)
|
||||||
|
GIT_DIRTY=$(git diff-index --quiet HEAD -- && echo "clean" || echo "dirty")
|
||||||
|
GIT_TAG=$(git describe --tags --always || echo "unknown")
|
||||||
|
GIT_COMMITS=$(git rev-list --count HEAD)
|
||||||
|
|
||||||
|
echo "Branch: $BRANCH"
|
||||||
|
echo "Tag: $GIT_TAG"
|
||||||
|
|
||||||
|
sed -i \
|
||||||
|
-e "s|@GIT_COMMIT_HASH@|$COMMIT_HASH|" \
|
||||||
|
-e "s|@GIT_BRANCH@|$BRANCH|" \
|
||||||
|
-e "s|@GIT_COMMIT_MESSAGE@|$COMMIT_MSG|" \
|
||||||
|
-e "s|@GIT_COMMIT_DATE@|$COMMIT_DATE|" \
|
||||||
|
-e "s|@GIT_DIRTY@|$GIT_DIRTY|" \
|
||||||
|
-e "s|@GIT_TAG@|$GIT_TAG|" \
|
||||||
|
-e "s|@GIT_COMMITS@|$GIT_COMMITS|" \
|
||||||
|
src/version.h.in
|
||||||
|
|
||||||
- name: Create tarball with submodules
|
- name: Create tarball with submodules
|
||||||
id: tar
|
id: tar
|
||||||
|
|
|
||||||
139
.github/workflows/translation-ai-check.yml
vendored
Normal file
139
.github/workflows/translation-ai-check.yml
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
name: AI Translation Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
# pull_request_target:
|
||||||
|
# types:
|
||||||
|
# - opened
|
||||||
|
issue_comment:
|
||||||
|
types:
|
||||||
|
- created
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
name: Review Translation
|
||||||
|
if: ${{ github.event_name == 'pull_request_target' || (github.event_name == 'issue_comment' && github.event.action == 'created' && github.event.issue.pull_request != null && github.event.comment.user.login == 'vaxerski' && github.event.comment.body == 'ai, please recheck' ) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
OPENAI_MODEL: gpt-5-mini
|
||||||
|
SYSTEM_PROMPT: |
|
||||||
|
You are a programmer and a translator. Your job is to review the attached patch for adding translation to a piece of software and make sure the submitted translation is not malicious, and that it makes sense. If the translation is not malicious, and doesn't contain obvious grammatical mistakes, say "Translation check OK". Otherwise, say "Translation check not ok" and list bad entries.
|
||||||
|
Examples of bad translations include obvious trolling (slurs, etc) or nonsense sentences. Meaningful improvements may be suggested, but if there are only minor improvements, just reply with "Translation check OK". Do not provide anything but the result and (if applicable) the bad entries or improvements.
|
||||||
|
|
||||||
|
AI_PROMPT: Translation patch below.
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout source code
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- uses: dorny/paths-filter@v3
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
i18n:
|
||||||
|
- 'src/i18n/**'
|
||||||
|
|
||||||
|
- name: Stop if i18n not changed
|
||||||
|
if: steps.changes.outputs.i18n != 'true'
|
||||||
|
run: echo "No i18n changes in this PR; skipping." && exit 0
|
||||||
|
|
||||||
|
- name: Determine PR number
|
||||||
|
id: pr
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
|
||||||
|
echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Download combined PR diff
|
||||||
|
id: get_diff
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
PR_NUMBER: ${{ steps.pr.outputs.number }}
|
||||||
|
run: |
|
||||||
|
# Get the combined diff for the entire PR
|
||||||
|
curl -sSL \
|
||||||
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-H "Accept: application/vnd.github.v3.diff" \
|
||||||
|
"https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" \
|
||||||
|
-o pr.diff
|
||||||
|
|
||||||
|
# Compute character length
|
||||||
|
LEN=$(wc -c < pr.diff | tr -d ' ')
|
||||||
|
echo "len=$LEN" >> "$GITHUB_OUTPUT"
|
||||||
|
if [ "$LEN" -gt 25000 ]; then
|
||||||
|
echo "too_long=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "too_long=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "got diff:"
|
||||||
|
cat pr.diff
|
||||||
|
|
||||||
|
- name: Comment when diff length exceeded
|
||||||
|
if: steps.get_diff.outputs.too_long == 'true'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
PR_NUMBER: ${{ steps.pr.outputs.number }}
|
||||||
|
run: |
|
||||||
|
jq -n --arg body "Diff length exceeded, can't query API" '{body: ("AI translation check result:\n\n" + $body)}' > body.json
|
||||||
|
curl -sS -X POST \
|
||||||
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \
|
||||||
|
--data @body.json
|
||||||
|
|
||||||
|
- name: Query OpenAI and post review
|
||||||
|
if: steps.get_diff.outputs.too_long == 'false'
|
||||||
|
env:
|
||||||
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||||
|
OPENAI_MODEL: ${{ env.OPENAI_MODEL }}
|
||||||
|
SYSTEM_PROMPT: ${{ env.SYSTEM_PROMPT }}
|
||||||
|
AI_PROMPT: ${{ env.AI_PROMPT }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
PR_NUMBER: ${{ steps.pr.outputs.number }}
|
||||||
|
run: |
|
||||||
|
# Prepare OpenAI chat request payload (embed diff safely)
|
||||||
|
jq -n \
|
||||||
|
--arg model "$OPENAI_MODEL" \
|
||||||
|
--arg sys "$SYSTEM_PROMPT" \
|
||||||
|
--arg prompt "$AI_PROMPT" \
|
||||||
|
--rawfile diff pr.diff \
|
||||||
|
'{model:$model,
|
||||||
|
messages:[
|
||||||
|
{role:"system", content:$sys},
|
||||||
|
{role:"user", content: ($prompt + "\n\n```diff\n" + $diff + "\n```")}
|
||||||
|
]
|
||||||
|
}' > payload.json
|
||||||
|
|
||||||
|
# Call OpenAI
|
||||||
|
curl -sS https://api.openai.com/v1/chat/completions \
|
||||||
|
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d @payload.json > response.json
|
||||||
|
|
||||||
|
# Extract response text
|
||||||
|
COMMENT=$(jq -r '.choices[0].message.content // empty' response.json)
|
||||||
|
if [ -z "$COMMENT" ]; then
|
||||||
|
COMMENT="AI did not return a response."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If failed, add a note
|
||||||
|
ADDITIONAL_NOTE=""
|
||||||
|
if [[ "$COMMENT" == *"not ok"* ]]; then
|
||||||
|
ADDITIONAL_NOTE=$(echo -ne "\n\nPlease note this check is a guideline, not a hard requirement. It is here to help you translate. If you disagree with some points, just state that. Any typos should be fixed.")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Post the review as a PR comment
|
||||||
|
jq -n --arg body "$COMMENT" --arg note "$ADDITIONAL_NOTE" '{body: ("AI translation check result:\n\n" + $body + $note)}' > body.json
|
||||||
|
echo "CURLing https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments"
|
||||||
|
curl -sS -X POST \
|
||||||
|
-H "Authorization: token $GITHUB_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/comments" \
|
||||||
|
--data @body.json
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -32,6 +32,8 @@ src/render/shaders/*.inc
|
||||||
src/render/shaders/Shaders.hpp
|
src/render/shaders/Shaders.hpp
|
||||||
|
|
||||||
hyprctl/hyprctl
|
hyprctl/hyprctl
|
||||||
|
hyprctl/hw-protocols/*.c*
|
||||||
|
hyprctl/hw-protocols/*.h*
|
||||||
|
|
||||||
gmon.out
|
gmon.out
|
||||||
*.out
|
*.out
|
||||||
|
|
@ -42,6 +44,7 @@ PKGBUILD
|
||||||
src/version.h
|
src/version.h
|
||||||
hyprpm/Makefile
|
hyprpm/Makefile
|
||||||
hyprctl/Makefile
|
hyprctl/Makefile
|
||||||
|
example/hyprland.desktop
|
||||||
|
|
||||||
**/.#*.*
|
**/.#*.*
|
||||||
**/#*.*#
|
**/#*.*#
|
||||||
|
|
|
||||||
272
CMakeLists.txt
272
CMakeLists.txt
|
|
@ -17,7 +17,6 @@ set(HYPRLAND_VERSION ${VER})
|
||||||
set(PREFIX ${CMAKE_INSTALL_PREFIX})
|
set(PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||||
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
set(INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
set(BINDIR ${CMAKE_INSTALL_BINDIR})
|
set(BINDIR ${CMAKE_INSTALL_BINDIR})
|
||||||
configure_file(hyprland.pc.in hyprland.pc @ONLY)
|
|
||||||
|
|
||||||
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
|
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
|
||||||
|
|
||||||
|
|
@ -25,7 +24,14 @@ message(STATUS "Gathering git info")
|
||||||
|
|
||||||
# Make shader files includable
|
# Make shader files includable
|
||||||
execute_process(COMMAND ./scripts/generateShaderIncludes.sh
|
execute_process(COMMAND ./scripts/generateShaderIncludes.sh
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
RESULT_VARIABLE HYPR_SHADER_GEN_RESULT)
|
||||||
|
if(NOT HYPR_SHADER_GEN_RESULT EQUAL 0)
|
||||||
|
message(
|
||||||
|
FATAL_ERROR
|
||||||
|
"Failed to generate shader includes (scripts/generateShaderIncludes.sh), exit code: ${HYPR_SHADER_GEN_RESULT}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
|
@ -33,11 +39,23 @@ find_package(PkgConfig REQUIRED)
|
||||||
# provide a .pc file and won't be detected this way
|
# provide a .pc file and won't be detected this way
|
||||||
pkg_check_modules(udis_dep IMPORTED_TARGET udis86>=1.7.2)
|
pkg_check_modules(udis_dep IMPORTED_TARGET udis86>=1.7.2)
|
||||||
|
|
||||||
# Fallback to subproject
|
# Find non-pkgconfig udis86, otherwise fallback to subproject
|
||||||
if(NOT udis_dep_FOUND)
|
if(NOT udis_dep_FOUND)
|
||||||
|
find_library(udis_nopc udis86)
|
||||||
|
if(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND"))
|
||||||
|
message(STATUS "Found udis86 at ${udis_nopc}")
|
||||||
|
else()
|
||||||
add_subdirectory("subprojects/udis86")
|
add_subdirectory("subprojects/udis86")
|
||||||
include_directories("subprojects/udis86")
|
include_directories("subprojects/udis86")
|
||||||
message(STATUS "udis86 dependency not found, falling back to subproject")
|
message(STATUS "udis86 dependency not found, falling back to subproject")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_library(librt rt)
|
||||||
|
if("${librt}" MATCHES "librt-NOTFOUND")
|
||||||
|
unset(LIBRT)
|
||||||
|
else()
|
||||||
|
set(LIBRT rt)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE)
|
if(CMAKE_BUILD_TYPE)
|
||||||
|
|
@ -68,9 +86,11 @@ message(
|
||||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||||
message(STATUS "Configuring Hyprland in Debug with CMake")
|
message(STATUS "Configuring Hyprland in Debug with CMake")
|
||||||
add_compile_definitions(HYPRLAND_DEBUG)
|
add_compile_definitions(HYPRLAND_DEBUG)
|
||||||
|
set(BUILD_TESTING ON)
|
||||||
else()
|
else()
|
||||||
add_compile_options(-O3)
|
add_compile_options(-O3)
|
||||||
message(STATUS "Configuring Hyprland in Release with CMake")
|
message(STATUS "Configuring Hyprland in Release with CMake")
|
||||||
|
set(BUILD_TESTING OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
|
add_compile_definitions(HYPRLAND_VERSION="${HYPRLAND_VERSION}")
|
||||||
|
|
@ -90,6 +110,7 @@ add_compile_options(
|
||||||
-Wno-narrowing
|
-Wno-narrowing
|
||||||
-Wno-pointer-arith
|
-Wno-pointer-arith
|
||||||
-Wno-clobbered
|
-Wno-clobbered
|
||||||
|
-frtti
|
||||||
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
|
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
|
||||||
|
|
||||||
# disable lto as it may break plugins
|
# disable lto as it may break plugins
|
||||||
|
|
@ -104,12 +125,19 @@ find_package(Threads REQUIRED)
|
||||||
|
|
||||||
set(GLES_VERSION "GLES3")
|
set(GLES_VERSION "GLES3")
|
||||||
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
||||||
|
find_package(glslang CONFIG REQUIRED)
|
||||||
|
|
||||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.3)
|
set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
|
||||||
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
|
set(HYPRLANG_MINIMUM_VERSION 0.6.7)
|
||||||
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
|
set(HYPRCURSOR_MINIMUM_VERSION 0.1.7)
|
||||||
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.8.2)
|
set(HYPRUTILS_MINIMUM_VERSION 0.11.0)
|
||||||
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.6)
|
set(HYPRGRAPHICS_MINIMUM_VERSION 0.1.6)
|
||||||
|
|
||||||
|
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=${AQUAMARINE_MINIMUM_VERSION})
|
||||||
|
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=${HYPRLANG_MINIMUM_VERSION})
|
||||||
|
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=${HYPRCURSOR_MINIMUM_VERSION})
|
||||||
|
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=${HYPRUTILS_MINIMUM_VERSION})
|
||||||
|
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=${HYPRGRAPHICS_MINIMUM_VERSION})
|
||||||
|
|
||||||
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
|
string(REPLACE "." ";" AQ_VERSION_LIST ${aquamarine_dep_VERSION})
|
||||||
list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR)
|
list(GET AQ_VERSION_LIST 0 AQ_VERSION_MAJOR)
|
||||||
|
|
@ -128,13 +156,41 @@ set(HYPRGRAPHICS_VERSION "${hyprgraphics_dep_VERSION}")
|
||||||
|
|
||||||
find_package(Git QUIET)
|
find_package(Git QUIET)
|
||||||
|
|
||||||
set(GIT_COMMIT_HASH "unknown")
|
# Populate variables with env vars if present
|
||||||
set(GIT_BRANCH "unknown")
|
set(GIT_COMMIT_HASH "$ENV{GIT_COMMIT_HASH}")
|
||||||
set(GIT_COMMIT_MESSAGE "unknown")
|
if(NOT GIT_COMMIT_HASH)
|
||||||
set(GIT_COMMIT_DATE "unknown")
|
set(GIT_COMMIT_HASH "unknown")
|
||||||
set(GIT_DIRTY "unknown")
|
endif()
|
||||||
set(GIT_TAG "unknown")
|
|
||||||
set(GIT_COMMITS "0")
|
set(GIT_BRANCH "$ENV{GIT_BRANCH}")
|
||||||
|
if(NOT GIT_BRANCH)
|
||||||
|
set(GIT_BRANCH "unknown")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GIT_COMMIT_MESSAGE "$ENV{GIT_COMMIT_MESSAGE}")
|
||||||
|
if(NOT GIT_COMMIT_MESSAGE)
|
||||||
|
set(GIT_COMMIT_MESSAGE "unknown")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GIT_COMMIT_DATE "$ENV{GIT_COMMIT_DATE}")
|
||||||
|
if(NOT GIT_COMMIT_DATE)
|
||||||
|
set(GIT_COMMIT_DATE "unknown")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GIT_DIRTY "$ENV{GIT_DIRTY}")
|
||||||
|
if(NOT GIT_DIRTY)
|
||||||
|
set(GIT_DIRTY "unknown")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GIT_TAG "$ENV{GIT_TAG}")
|
||||||
|
if(NOT GIT_TAG)
|
||||||
|
set(GIT_TAG "unknown")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(GIT_COMMITS "$ENV{GIT_COMMITS}")
|
||||||
|
if(NOT GIT_COMMITS)
|
||||||
|
set(GIT_COMMITS "0")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(Git_FOUND)
|
if(Git_FOUND)
|
||||||
execute_process(
|
execute_process(
|
||||||
|
|
@ -155,10 +211,10 @@ if(Git_FOUND)
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current
|
execute_process(COMMAND ${GIT_EXECUTABLE} branch --show-current
|
||||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||||
OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
|
OUTPUT_VARIABLE GIT_BRANCH OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%s
|
execute_process(COMMAND sh "-c" "${GIT_EXECUTABLE} show -s --format=%s --no-show-signature | sed \"s/\\\"/\'/g\""
|
||||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||||
OUTPUT_VARIABLE GIT_COMMIT_MESSAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
OUTPUT_VARIABLE GIT_COMMIT_MESSAGE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%cd --date=local
|
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format=%cd --date=local --no-show-signature
|
||||||
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
WORKING_DIRECTORY ${GIT_TOPLEVEL}
|
||||||
OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
OUTPUT_VARIABLE GIT_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --quiet HEAD --
|
execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --quiet HEAD --
|
||||||
|
|
@ -188,28 +244,37 @@ configure_file(
|
||||||
|
|
||||||
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/version.h PROPERTIES GENERATED TRUE)
|
set_source_files_properties(${CMAKE_SOURCE_DIR}/src/version.h PROPERTIES GENERATED TRUE)
|
||||||
|
|
||||||
|
set(XKBCOMMON_MINIMUM_VERSION 1.11.0)
|
||||||
|
set(WAYLAND_SERVER_MINIMUM_VERSION 1.22.91)
|
||||||
|
set(WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION 1.45)
|
||||||
|
set(LIBINPUT_MINIMUM_VERSION 1.28)
|
||||||
|
|
||||||
pkg_check_modules(
|
pkg_check_modules(
|
||||||
deps
|
deps
|
||||||
REQUIRED
|
REQUIRED
|
||||||
IMPORTED_TARGET
|
IMPORTED_TARGET GLOBAL
|
||||||
xkbcommon
|
xkbcommon>=${XKBCOMMON_MINIMUM_VERSION}
|
||||||
uuid
|
uuid
|
||||||
wayland-server>=1.22.90
|
wayland-server>=${WAYLAND_SERVER_MINIMUM_VERSION}
|
||||||
wayland-protocols>=1.45
|
wayland-protocols>=${WAYLAND_SERVER_PROTOCOLS_MINIMUM_VERSION}
|
||||||
cairo
|
cairo
|
||||||
pango
|
pango
|
||||||
pangocairo
|
pangocairo
|
||||||
pixman-1
|
pixman-1
|
||||||
xcursor
|
xcursor
|
||||||
libdrm
|
libdrm
|
||||||
libinput>=1.28
|
libinput>=${LIBINPUT_MINIMUM_VERSION}
|
||||||
gbm
|
gbm
|
||||||
gio-2.0
|
gio-2.0
|
||||||
re2)
|
re2
|
||||||
|
muparser
|
||||||
|
lcms2)
|
||||||
|
|
||||||
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
|
find_package(hyprwayland-scanner 0.3.10 REQUIRED)
|
||||||
|
|
||||||
file(GLOB_RECURSE SRCFILES "src/*.cpp")
|
file(GLOB_RECURSE SRCFILES "src/*.cpp")
|
||||||
|
get_filename_component(FULL_MAIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp ABSOLUTE)
|
||||||
|
list(REMOVE_ITEM SRCFILES "${FULL_MAIN_PATH}")
|
||||||
|
|
||||||
set(TRACY_CPP_FILES "")
|
set(TRACY_CPP_FILES "")
|
||||||
if(USE_TRACY)
|
if(USE_TRACY)
|
||||||
|
|
@ -217,7 +282,12 @@ if(USE_TRACY)
|
||||||
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
|
message(STATUS "Tracy enabled, TRACY_CPP_FILES: " ${TRACY_CPP_FILES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
|
add_library(hyprland_lib STATIC ${SRCFILES})
|
||||||
|
add_executable(Hyprland src/main.cpp ${TRACY_CPP_FILES})
|
||||||
|
target_link_libraries(Hyprland hyprland_lib)
|
||||||
|
|
||||||
|
target_include_directories(hyprland_lib PUBLIC ${deps_INCLUDE_DIRS})
|
||||||
|
target_include_directories(Hyprland PUBLIC ${deps_INCLUDE_DIRS})
|
||||||
|
|
||||||
set(USE_GPROF OFF)
|
set(USE_GPROF OFF)
|
||||||
|
|
||||||
|
|
@ -227,23 +297,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||||
if(WITH_ASAN)
|
if(WITH_ASAN)
|
||||||
message(STATUS "Enabling ASan")
|
message(STATUS "Enabling ASan")
|
||||||
|
|
||||||
target_link_libraries(Hyprland asan)
|
target_link_libraries(hyprland_lib PUBLIC asan)
|
||||||
target_compile_options(Hyprland PUBLIC -fsanitize=address)
|
target_compile_options(hyprland_lib PUBLIC -fsanitize=address)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(USE_TRACY)
|
|
||||||
message(STATUS "Tracy is turned on")
|
|
||||||
|
|
||||||
option(TRACY_ENABLE "" ON)
|
|
||||||
option(TRACY_ON_DEMAND "" ON)
|
|
||||||
add_subdirectory(subprojects/tracy)
|
|
||||||
|
|
||||||
target_link_libraries(Hyprland Tracy::TracyClient)
|
|
||||||
|
|
||||||
if(USE_TRACY_GPU)
|
|
||||||
message(STATUS "Tracy GPU Profiling is turned on")
|
|
||||||
add_compile_definitions(USE_TRACY_GPU)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_compile_options(-fno-pie -fno-builtin)
|
add_compile_options(-fno-pie -fno-builtin)
|
||||||
|
|
@ -254,6 +309,27 @@ if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(USE_TRACY)
|
||||||
|
message(STATUS "Tracy is turned on")
|
||||||
|
|
||||||
|
option(TRACY_ENABLE "" ON)
|
||||||
|
option(TRACY_ON_DEMAND "" ON)
|
||||||
|
add_subdirectory(subprojects/tracy)
|
||||||
|
|
||||||
|
add_compile_options(-fno-omit-frame-pointer)
|
||||||
|
|
||||||
|
target_link_libraries(hyprland_lib PUBLIC Tracy::TracyClient)
|
||||||
|
|
||||||
|
if(USE_TRACY_GPU)
|
||||||
|
message(STATUS "Tracy GPU Profiling is turned on")
|
||||||
|
add_compile_definitions(USE_TRACY_GPU)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILT_WITH_NIX)
|
||||||
|
add_compile_definitions(BUILT_WITH_NIX)
|
||||||
|
endif()
|
||||||
|
|
||||||
check_include_file("execinfo.h" EXECINFOH)
|
check_include_file("execinfo.h" EXECINFOH)
|
||||||
if(EXECINFOH)
|
if(EXECINFOH)
|
||||||
message(STATUS "Configuration supports execinfo")
|
message(STATUS "Configuration supports execinfo")
|
||||||
|
|
@ -263,19 +339,19 @@ endif()
|
||||||
include(CheckLibraryExists)
|
include(CheckLibraryExists)
|
||||||
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
|
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
|
||||||
if(HAVE_LIBEXECINFO)
|
if(HAVE_LIBEXECINFO)
|
||||||
target_link_libraries(Hyprland execinfo)
|
target_link_libraries(hyprland_lib PUBLIC execinfo)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_include_file("sys/timerfd.h" HAS_TIMERFD)
|
check_include_file("sys/timerfd.h" HAS_TIMERFD)
|
||||||
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
|
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
|
||||||
if(NOT HAS_TIMERFD AND epoll_FOUND)
|
if(NOT HAS_TIMERFD AND epoll_FOUND)
|
||||||
target_link_libraries(Hyprland PkgConfig::epoll)
|
target_link_libraries(hyprland_lib PUBLIC PkgConfig::epoll)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_include_file("sys/inotify.h" HAS_INOTIFY)
|
check_include_file("sys/inotify.h" HAS_INOTIFY)
|
||||||
pkg_check_modules(inotify IMPORTED_TARGET libinotify)
|
pkg_check_modules(inotify IMPORTED_TARGET libinotify)
|
||||||
if(NOT HAS_INOTIFY AND inotify_FOUND)
|
if(NOT HAS_INOTIFY AND inotify_FOUND)
|
||||||
target_link_libraries(Hyprland PkgConfig::inotify)
|
target_link_libraries(hyprland_lib PUBLIC PkgConfig::inotify)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NO_XWAYLAND)
|
if(NO_XWAYLAND)
|
||||||
|
|
@ -283,10 +359,7 @@ if(NO_XWAYLAND)
|
||||||
add_compile_definitions(NO_XWAYLAND)
|
add_compile_definitions(NO_XWAYLAND)
|
||||||
else()
|
else()
|
||||||
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
|
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
|
||||||
pkg_check_modules(
|
set(XWAYLAND_DEPENDENCIES
|
||||||
xdeps
|
|
||||||
REQUIRED
|
|
||||||
IMPORTED_TARGET
|
|
||||||
xcb
|
xcb
|
||||||
xcb-render
|
xcb-render
|
||||||
xcb-xfixes
|
xcb-xfixes
|
||||||
|
|
@ -294,9 +367,21 @@ else()
|
||||||
xcb-composite
|
xcb-composite
|
||||||
xcb-res
|
xcb-res
|
||||||
xcb-errors)
|
xcb-errors)
|
||||||
target_link_libraries(Hyprland PkgConfig::xdeps)
|
|
||||||
|
pkg_check_modules(
|
||||||
|
xdeps
|
||||||
|
REQUIRED
|
||||||
|
IMPORTED_TARGET
|
||||||
|
${XWAYLAND_DEPENDENCIES})
|
||||||
|
|
||||||
|
string(JOIN ", " PKGCONFIG_XWAYLAND_DEPENDENCIES ${XWAYLAND_DEPENDENCIES})
|
||||||
|
string(PREPEND PKGCONFIG_XWAYLAND_DEPENDENCIES ", ")
|
||||||
|
|
||||||
|
target_link_libraries(hyprland_lib PUBLIC PkgConfig::xdeps)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
configure_file(hyprland.pc.in hyprland.pc @ONLY)
|
||||||
|
|
||||||
if(NO_SYSTEMD)
|
if(NO_SYSTEMD)
|
||||||
message(STATUS "SYSTEMD support is disabled...")
|
message(STATUS "SYSTEMD support is disabled...")
|
||||||
else()
|
else()
|
||||||
|
|
@ -321,29 +406,38 @@ if(CMAKE_DISABLE_PRECOMPILE_HEADERS)
|
||||||
message(STATUS "Not using precompiled headers")
|
message(STATUS "Not using precompiled headers")
|
||||||
else()
|
else()
|
||||||
message(STATUS "Setting precompiled headers")
|
message(STATUS "Setting precompiled headers")
|
||||||
target_precompile_headers(Hyprland PRIVATE
|
target_precompile_headers(hyprland_lib PRIVATE
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:src/pch/pch.hpp>)
|
$<$<COMPILE_LANGUAGE:CXX>:src/pch/pch.hpp>)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "Setting link libraries")
|
message(STATUS "Setting link libraries")
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
Hyprland
|
hyprland_lib
|
||||||
rt
|
PUBLIC
|
||||||
PkgConfig::aquamarine_dep
|
PkgConfig::aquamarine_dep
|
||||||
PkgConfig::hyprlang_dep
|
PkgConfig::hyprlang_dep
|
||||||
PkgConfig::hyprutils_dep
|
PkgConfig::hyprutils_dep
|
||||||
PkgConfig::hyprcursor_dep
|
PkgConfig::hyprcursor_dep
|
||||||
PkgConfig::hyprgraphics_dep
|
PkgConfig::hyprgraphics_dep
|
||||||
PkgConfig::deps)
|
PkgConfig::deps
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(
|
||||||
|
Hyprland
|
||||||
|
${LIBRT}
|
||||||
|
hyprland_lib)
|
||||||
if(udis_dep_FOUND)
|
if(udis_dep_FOUND)
|
||||||
target_link_libraries(Hyprland PkgConfig::udis_dep)
|
target_link_libraries(hyprland_lib PUBLIC PkgConfig::udis_dep)
|
||||||
|
elseif(NOT("${udis_nopc}" MATCHES "udis_nopc-NOTFOUND"))
|
||||||
|
target_link_libraries(hyprland_lib PUBLIC ${udis_nopc})
|
||||||
else()
|
else()
|
||||||
target_link_libraries(Hyprland libudis86)
|
target_link_libraries(hyprland_lib PUBLIC libudis86)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# used by `make installheaders`, to ensure the headers are generated
|
# used by `make installheaders`, to ensure the headers are generated
|
||||||
add_custom_target(generate-protocol-headers)
|
add_custom_target(generate-protocol-headers)
|
||||||
|
set(PROTOCOL_SOURCES "")
|
||||||
|
|
||||||
function(protocolnew protoPath protoName external)
|
function(protocolnew protoPath protoName external)
|
||||||
if(external)
|
if(external)
|
||||||
|
|
@ -357,10 +451,15 @@ function(protocolnew protoPath protoName external)
|
||||||
COMMAND hyprwayland-scanner ${path}/${protoName}.xml
|
COMMAND hyprwayland-scanner ${path}/${protoName}.xml
|
||||||
${CMAKE_SOURCE_DIR}/protocols/
|
${CMAKE_SOURCE_DIR}/protocols/
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||||
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp
|
target_sources(hyprland_lib PRIVATE protocols/${protoName}.cpp
|
||||||
protocols/${protoName}.hpp)
|
protocols/${protoName}.hpp)
|
||||||
target_sources(generate-protocol-headers
|
target_sources(generate-protocol-headers
|
||||||
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp)
|
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp)
|
||||||
|
|
||||||
|
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp")
|
||||||
|
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||||
|
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp")
|
||||||
|
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
function(protocolWayland)
|
function(protocolWayland)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|
@ -370,12 +469,21 @@ function(protocolWayland)
|
||||||
hyprwayland-scanner --wayland-enums
|
hyprwayland-scanner --wayland-enums
|
||||||
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
|
${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||||
target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
|
target_sources(hyprland_lib PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
|
||||||
target_sources(generate-protocol-headers
|
target_sources(generate-protocol-headers
|
||||||
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
|
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
|
||||||
|
|
||||||
|
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.hpp")
|
||||||
|
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||||
|
list(APPEND PROTOCOL_SOURCES "${CMAKE_SOURCE_DIR}/protocols/wayland.cpp")
|
||||||
|
set(PROTOCOL_SOURCES "${PROTOCOL_SOURCES}" PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads)
|
if(TARGET OpenGL::GL)
|
||||||
|
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
|
||||||
|
else()
|
||||||
|
target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads)
|
||||||
|
endif()
|
||||||
|
|
||||||
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
|
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)
|
||||||
if(hyprland_protocols_dep_FOUND)
|
if(hyprland_protocols_dep_FOUND)
|
||||||
|
|
@ -403,8 +511,6 @@ protocolnew("protocols" "kde-server-decoration" true)
|
||||||
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
|
protocolnew("protocols" "wlr-data-control-unstable-v1" true)
|
||||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
|
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-focus-grab-v1" true)
|
||||||
protocolnew("protocols" "wlr-layer-shell-unstable-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("protocols" "wayland-drm" true)
|
||||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
|
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-ctm-control-v1" true)
|
||||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)
|
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-surface-v1" true)
|
||||||
|
|
@ -452,11 +558,14 @@ protocolnew("staging/ext-data-control" "ext-data-control-v1" false)
|
||||||
protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
|
protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
|
||||||
protocolnew("staging/fifo" "fifo-v1" false)
|
protocolnew("staging/fifo" "fifo-v1" false)
|
||||||
protocolnew("staging/commit-timing" "commit-timing-v1" false)
|
protocolnew("staging/commit-timing" "commit-timing-v1" false)
|
||||||
|
protocolnew("staging/ext-image-capture-source" "ext-image-capture-source-v1" false)
|
||||||
|
protocolnew("staging/ext-image-copy-capture" "ext-image-copy-capture-v1" false)
|
||||||
|
|
||||||
protocolwayland()
|
protocolwayland()
|
||||||
|
|
||||||
# tools
|
# tools
|
||||||
add_subdirectory(hyprctl)
|
add_subdirectory(hyprctl)
|
||||||
|
add_subdirectory(start)
|
||||||
|
|
||||||
if(NO_HYPRPM)
|
if(NO_HYPRPM)
|
||||||
message(STATUS "hyprpm is disabled")
|
message(STATUS "hyprpm is disabled")
|
||||||
|
|
@ -475,6 +584,11 @@ install(
|
||||||
\"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
|
\"\$ENV{DESTDIR}${CMAKE_INSTALL_FULL_BINDIR}/hyprland\" \
|
||||||
)")
|
)")
|
||||||
# session file
|
# session file
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_SOURCE_DIR}/example/hyprland.desktop.in
|
||||||
|
${CMAKE_SOURCE_DIR}/example/hyprland.desktop
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
|
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions)
|
||||||
|
|
||||||
|
|
@ -483,7 +597,6 @@ add_compile_definitions(DATAROOTDIR="${CMAKE_INSTALL_FULL_DATAROOTDIR}")
|
||||||
|
|
||||||
# installable assets
|
# installable assets
|
||||||
file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*")
|
file(GLOB_RECURSE INSTALLABLE_ASSETS "assets/install/*")
|
||||||
list(FILTER INSTALLABLE_ASSETS EXCLUDE REGEX "meson.build")
|
|
||||||
install(FILES ${INSTALLABLE_ASSETS}
|
install(FILES ${INSTALLABLE_ASSETS}
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/hypr)
|
||||||
|
|
||||||
|
|
@ -521,10 +634,37 @@ install(
|
||||||
PATTERN "*.hpp"
|
PATTERN "*.hpp"
|
||||||
PATTERN "*.inc")
|
PATTERN "*.inc")
|
||||||
|
|
||||||
if(BUILD_TESTING OR BUILD_HYPRTESTER)
|
if(BUILD_TESTING OR WITH_TESTS)
|
||||||
message(STATUS "Building hyprtester")
|
message(STATUS "Building tests")
|
||||||
|
|
||||||
|
# hyprtester
|
||||||
add_subdirectory(hyprtester)
|
add_subdirectory(hyprtester)
|
||||||
|
|
||||||
|
# GTest
|
||||||
|
find_package(GTest CONFIG REQUIRED)
|
||||||
|
include(GoogleTest)
|
||||||
|
file(GLOB_RECURSE TESTFILES "tests/*.cpp")
|
||||||
|
add_executable(hyprland_gtests ${TESTFILES})
|
||||||
|
target_compile_options(hyprland_gtests PRIVATE --coverage)
|
||||||
|
target_link_options(hyprland_gtests PRIVATE --coverage)
|
||||||
|
target_include_directories(
|
||||||
|
hyprland_gtests
|
||||||
|
PUBLIC "./include"
|
||||||
|
PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
|
||||||
|
|
||||||
|
target_link_libraries(hyprland_gtests hyprland_lib GTest::gtest_main)
|
||||||
|
|
||||||
|
gtest_discover_tests(hyprland_gtests)
|
||||||
|
|
||||||
|
# Enable coverage in main hyprland lib
|
||||||
|
target_compile_options(hyprland_lib PRIVATE --coverage)
|
||||||
|
target_link_options(hyprland_lib PRIVATE --coverage)
|
||||||
|
target_link_libraries(hyprland_lib PUBLIC gcov)
|
||||||
|
|
||||||
|
# Enable coverage in hyprland exe
|
||||||
|
target_compile_options(Hyprland PRIVATE --coverage)
|
||||||
|
target_link_options(Hyprland PRIVATE --coverage)
|
||||||
|
target_link_libraries(Hyprland gcov)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
|
|
@ -533,12 +673,8 @@ if(BUILD_TESTING)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_custom_target(tests)
|
add_custom_target(tests)
|
||||||
|
|
||||||
add_test(
|
add_dependencies(tests hyprland_gtests)
|
||||||
NAME "Main Test"
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hyprtester
|
|
||||||
COMMAND hyprtester)
|
|
||||||
|
|
||||||
add_dependencies(tests hyprtester)
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Testing is disabled")
|
message(STATUS "Testing is disabled")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
BSD 3-Clause License
|
BSD 3-Clause License
|
||||||
|
|
||||||
Copyright (c) 2022-2025, vaxerski
|
Copyright (c) 2022-2026, vaxerski
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
|
||||||
3
Makefile
3
Makefile
|
|
@ -18,6 +18,7 @@ nopch:
|
||||||
clear:
|
clear:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp
|
rm -f ./protocols/*.h ./protocols/*.c ./protocols/*.cpp ./protocols/*.hpp
|
||||||
|
rm -f ./hyprctl/hw-protocols/*.cpp ./hyprctl/hw-protocols/*.hpp
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(MAKE) clear
|
$(MAKE) clear
|
||||||
|
|
@ -87,7 +88,7 @@ asan:
|
||||||
@echo "Wayland done"
|
@echo "Wayland done"
|
||||||
|
|
||||||
patch -p1 < ./scripts/hyprlandStaticAsan.diff
|
patch -p1 < ./scripts/hyprlandStaticAsan.diff
|
||||||
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DWITH_ASAN:STRING=True -DUSE_TRACY:STRING=False -DUSE_TRACY_GPU:STRING=False -S . -B ./build -G Ninja
|
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DWITH_ASAN:STRING=True -DUSE_TRACY:STRING=False -DUSE_TRACY_GPU:STRING=False -S . -B ./build
|
||||||
cmake --build ./build --config Debug --target all
|
cmake --build ./build --config Debug --target all
|
||||||
@echo "Hyprland done"
|
@echo "Hyprland done"
|
||||||
|
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.52.0
|
0.54.0
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
globber = run_command('sh', '-c', 'find . -type f -not -name "*.build"', check: true)
|
|
||||||
files = globber.stdout().strip().split('\n')
|
|
||||||
|
|
||||||
foreach file : files
|
|
||||||
install_data(
|
|
||||||
file,
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'hypr'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
)
|
|
||||||
endforeach
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
install_data(
|
|
||||||
'hyprland-portals.conf',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'xdg-desktop-portal'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
)
|
|
||||||
|
|
||||||
subdir('install')
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
install_man('Hyprland.1')
|
|
||||||
install_man('hyprctl.1')
|
|
||||||
|
|
@ -27,7 +27,7 @@ monitor=,preferred,auto,auto
|
||||||
# Set programs that you use
|
# Set programs that you use
|
||||||
$terminal = kitty
|
$terminal = kitty
|
||||||
$fileManager = dolphin
|
$fileManager = dolphin
|
||||||
$menu = wofi --show drun
|
$menu = hyprlauncher
|
||||||
|
|
||||||
|
|
||||||
#################
|
#################
|
||||||
|
|
@ -159,10 +159,23 @@ animations {
|
||||||
# uncomment all if you wish to use that.
|
# uncomment all if you wish to use that.
|
||||||
# workspace = w[tv1], gapsout:0, gapsin:0
|
# workspace = w[tv1], gapsout:0, gapsin:0
|
||||||
# workspace = f[1], gapsout:0, gapsin:0
|
# workspace = f[1], gapsout:0, gapsin:0
|
||||||
# windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
|
# windowrule {
|
||||||
# windowrule = rounding 0, floating:0, onworkspace:w[tv1]
|
# name = no-gaps-wtv1
|
||||||
# windowrule = bordersize 0, floating:0, onworkspace:f[1]
|
# match:float = false
|
||||||
# windowrule = rounding 0, floating:0, onworkspace:f[1]
|
# match:workspace = w[tv1]
|
||||||
|
#
|
||||||
|
# border_size = 0
|
||||||
|
# rounding = 0
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# windowrule {
|
||||||
|
# name = no-gaps-f1
|
||||||
|
# match:float = false
|
||||||
|
# match:workspace = f[1]
|
||||||
|
#
|
||||||
|
# border_size = 0
|
||||||
|
# rounding = 0
|
||||||
|
# }
|
||||||
|
|
||||||
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
||||||
dwindle {
|
dwindle {
|
||||||
|
|
@ -224,12 +237,12 @@ $mainMod = SUPER # Sets "Windows" key as main modifier
|
||||||
# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
|
# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
|
||||||
bind = $mainMod, Q, exec, $terminal
|
bind = $mainMod, Q, exec, $terminal
|
||||||
bind = $mainMod, C, killactive,
|
bind = $mainMod, C, killactive,
|
||||||
bind = $mainMod, M, exit,
|
bind = $mainMod, M, exec, command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit
|
||||||
bind = $mainMod, E, exec, $fileManager
|
bind = $mainMod, E, exec, $fileManager
|
||||||
bind = $mainMod, V, togglefloating,
|
bind = $mainMod, V, togglefloating,
|
||||||
bind = $mainMod, R, exec, $menu
|
bind = $mainMod, R, exec, $menu
|
||||||
bind = $mainMod, P, pseudo, # dwindle
|
bind = $mainMod, P, pseudo, # dwindle
|
||||||
bind = $mainMod, J, togglesplit, # dwindle
|
bind = $mainMod, J, layoutmsg, togglesplit # dwindle
|
||||||
|
|
||||||
# Move focus with mainMod + arrow keys
|
# Move focus with mainMod + arrow keys
|
||||||
bind = $mainMod, left, movefocus, l
|
bind = $mainMod, left, movefocus, l
|
||||||
|
|
@ -294,11 +307,35 @@ bindl = , XF86AudioPrev, exec, playerctl previous
|
||||||
# See https://wiki.hypr.land/Configuring/Window-Rules/ for more
|
# See https://wiki.hypr.land/Configuring/Window-Rules/ for more
|
||||||
# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
|
# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules
|
||||||
|
|
||||||
# Example windowrule
|
# Example windowrules that are useful
|
||||||
# windowrule = float,class:^(kitty)$,title:^(kitty)$
|
|
||||||
|
|
||||||
# Ignore maximize requests from apps. You'll probably like this.
|
windowrule {
|
||||||
windowrule = suppressevent maximize, class:.*
|
# Ignore maximize requests from all apps. You'll probably like this.
|
||||||
|
name = suppress-maximize-events
|
||||||
|
match:class = .*
|
||||||
|
|
||||||
# Fix some dragging issues with XWayland
|
suppress_event = maximize
|
||||||
windowrule = nofocus,class:^$,title:^$,xwayland:1,floating:1,fullscreen:0,pinned:0
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
# Fix some dragging issues with XWayland
|
||||||
|
name = fix-xwayland-drags
|
||||||
|
match:class = ^$
|
||||||
|
match:title = ^$
|
||||||
|
match:xwayland = true
|
||||||
|
match:float = true
|
||||||
|
match:fullscreen = false
|
||||||
|
match:pin = false
|
||||||
|
|
||||||
|
no_focus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Hyprland-run windowrule
|
||||||
|
windowrule {
|
||||||
|
name = move-hyprland-run
|
||||||
|
|
||||||
|
match:class = hyprland-run
|
||||||
|
|
||||||
|
move = 20 monitor_h-120
|
||||||
|
float = yes
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=Hyprland
|
Name=Hyprland
|
||||||
Comment=An intelligent dynamic tiling Wayland compositor
|
Comment=An intelligent dynamic tiling Wayland compositor
|
||||||
Exec=Hyprland
|
Exec=@PREFIX@/@CMAKE_INSTALL_BINDIR@/start-hyprland
|
||||||
Type=Application
|
Type=Application
|
||||||
DesktopNames=Hyprland
|
DesktopNames=Hyprland
|
||||||
Keywords=tiling;wayland;compositor;
|
Keywords=tiling;wayland;compositor;
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
install_data(
|
|
||||||
'hyprland.conf',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'hypr'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
)
|
|
||||||
install_data(
|
|
||||||
'hyprland.desktop',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'wayland-sessions'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
)
|
|
||||||
100
flake.lock
generated
100
flake.lock
generated
|
|
@ -16,11 +16,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762356719,
|
"lastModified": 1772292445,
|
||||||
"narHash": "sha256-qwd/xdoOya1m8FENle+4hWnydCtlXUWLAW/Auk6WL7s=",
|
"narHash": "sha256-4F1Q7U313TKUDDovCC96m/Za4wZcJ3yqtu4eSrj8lk8=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "aquamarine",
|
"repo": "aquamarine",
|
||||||
"rev": "6d0b3567584691bf9d8fedb5d0093309e2f979c7",
|
"rev": "1dbbba659c1cef0b0202ce92cadfe13bae550e8f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -32,15 +32,15 @@
|
||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747046372,
|
"lastModified": 1767039857,
|
||||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||||
"owner": "edolstra",
|
"owner": "NixOS",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "edolstra",
|
"owner": "NixOS",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
@ -105,11 +105,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762462052,
|
"lastModified": 1770511807,
|
||||||
"narHash": "sha256-6roLYzcDf4V38RUMSqycsOwAnqfodL6BmhRkUtwIgdA=",
|
"narHash": "sha256-suKmSbSk34uPOJDTg/GbPrKEJutzK08vj0VoTvAFBCA=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprgraphics",
|
"repo": "hyprgraphics",
|
||||||
"rev": "ffc999d980c7b3bca85d3ebd0a9fbadf984a8162",
|
"rev": "7c75487edd43a71b61adb01cae8326d277aab683",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -133,6 +133,9 @@
|
||||||
"hyprutils": [
|
"hyprutils": [
|
||||||
"hyprutils"
|
"hyprutils"
|
||||||
],
|
],
|
||||||
|
"hyprwayland-scanner": [
|
||||||
|
"hyprwayland-scanner"
|
||||||
|
],
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
|
|
@ -141,11 +144,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762465111,
|
"lastModified": 1767023960,
|
||||||
"narHash": "sha256-dS13YZdWjgGGLBjpT4FHB6xf8I/WiAU+mgNWXsZgDUs=",
|
"narHash": "sha256-R2HgtVS1G3KSIKAQ77aOZ+Q0HituOmPgXW9nBNkpp3Q=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprland-guiutils",
|
"repo": "hyprland-guiutils",
|
||||||
"rev": "a415eba866a953f3096d661318f771aa0082eb98",
|
"rev": "c2e906261142f5dd1ee0bfc44abba23e2754c660",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -164,11 +167,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1759610243,
|
"lastModified": 1765214753,
|
||||||
"narHash": "sha256-+KEVnKBe8wz+a6dTLq8YDcF3UrhQElwsYJaVaHXJtoI=",
|
"narHash": "sha256-P9zdGXOzToJJgu5sVjv7oeOGPIIwrd9hAUAP3PsmBBs=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprland-protocols",
|
"repo": "hyprland-protocols",
|
||||||
"rev": "bd153e76f751f150a09328dbdeb5e4fab9d23622",
|
"rev": "3f3860b869014c00e8b9e0528c7b4ddc335c21ab",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -190,11 +193,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758927902,
|
"lastModified": 1771866172,
|
||||||
"narHash": "sha256-LZgMds7M94+vuMql2bERQ6LiFFdhgsEFezE4Vn+Ys3A=",
|
"narHash": "sha256-fYFoXhQLrm1rD8vSFKQBOEX4OGCuJdLt1amKfHd5GAw=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprlang",
|
"repo": "hyprlang",
|
||||||
"rev": "4dafa28d4f79877d67a7d1a654cddccf8ebf15da",
|
"rev": "0b219224910e7642eb0ed49f0db5ec3d008e3e41",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -221,7 +224,10 @@
|
||||||
"hyprland-guiutils",
|
"hyprland-guiutils",
|
||||||
"hyprutils"
|
"hyprutils"
|
||||||
],
|
],
|
||||||
"hyprwayland-scanner": "hyprwayland-scanner",
|
"hyprwayland-scanner": [
|
||||||
|
"hyprland-guiutils",
|
||||||
|
"hyprwayland-scanner"
|
||||||
|
],
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"hyprland-guiutils",
|
"hyprland-guiutils",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
|
|
@ -232,11 +238,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762463729,
|
"lastModified": 1764592794,
|
||||||
"narHash": "sha256-2fYkU/mdz8WKY3dkDPlE/j6hTxIwqultsx4gMMsMns0=",
|
"narHash": "sha256-7CcO+wbTJ1L1NBQHierHzheQGPWwkIQug/w+fhTAVuU=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprtoolkit",
|
"repo": "hyprtoolkit",
|
||||||
"rev": "88483bdee5329ec985f0c8f834c519cd18cfe532",
|
"rev": "5cfe0743f0e608e1462972303778d8a0859ee63e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -255,11 +261,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762387740,
|
"lastModified": 1771271487,
|
||||||
"narHash": "sha256-gQ9zJ+pUI4o+Gh4Z6jhJll7jjCSwi8ZqJIhCE2oqwhQ=",
|
"narHash": "sha256-41gEiUS0Pyw3L/ge1l8MXn61cK14VAhgWB/JV8s/oNI=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprutils",
|
"repo": "hyprutils",
|
||||||
"rev": "926689ddb9c0a8787e58c02c765a62e32d63d1f7",
|
"rev": "340a792e3b3d482c4ae5f66d27a9096bdee6d76d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -271,22 +277,18 @@
|
||||||
"hyprwayland-scanner": {
|
"hyprwayland-scanner": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"hyprland-guiutils",
|
|
||||||
"hyprtoolkit",
|
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"systems": [
|
"systems": [
|
||||||
"hyprland-guiutils",
|
|
||||||
"hyprtoolkit",
|
|
||||||
"systems"
|
"systems"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755184602,
|
"lastModified": 1770501770,
|
||||||
"narHash": "sha256-RCBQN8xuADB0LEgaKbfRqwm6CdyopE1xIEhNc67FAbw=",
|
"narHash": "sha256-NWRM6+YxTRv+bT9yvlhhJ2iLae1B1pNH3mAL5wi2rlQ=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprwayland-scanner",
|
"repo": "hyprwayland-scanner",
|
||||||
"rev": "b3b0f1f40ae09d4447c20608e5a4faf8bf3c492d",
|
"rev": "0bd8b6cde9ec27d48aad9e5b4deefb3746909d40",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -295,8 +297,11 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hyprwayland-scanner_2": {
|
"hyprwire": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"hyprutils": [
|
||||||
|
"hyprutils"
|
||||||
|
],
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
|
|
@ -305,26 +310,26 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755184602,
|
"lastModified": 1771606233,
|
||||||
"narHash": "sha256-RCBQN8xuADB0LEgaKbfRqwm6CdyopE1xIEhNc67FAbw=",
|
"narHash": "sha256-F3PLUqQ/TwgR70U+UeOqJnihJZ2EuunzojYC4g5xHr0=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprwayland-scanner",
|
"repo": "hyprwire",
|
||||||
"rev": "b3b0f1f40ae09d4447c20608e5a4faf8bf3c492d",
|
"rev": "06c7f1f8c4194786c8400653c4efc49dc14c0f3a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprwayland-scanner",
|
"repo": "hyprwire",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762363567,
|
"lastModified": 1772198003,
|
||||||
"narHash": "sha256-YRqMDEtSMbitIMj+JLpheSz0pwEr0Rmy5mC7myl17xs=",
|
"narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ae814fd3904b621d8ab97418f1d0f2eb0d3716f4",
|
"rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -343,11 +348,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762441963,
|
"lastModified": 1772024342,
|
||||||
"narHash": "sha256-j+rNQ119ffYUkYt2YYS6rnd6Jh/crMZmbqpkGLXaEt0=",
|
"narHash": "sha256-+eXlIc4/7dE6EcPs9a2DaSY3fTA9AE526hGqkNID3Wg=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "git-hooks.nix",
|
"repo": "git-hooks.nix",
|
||||||
"rev": "8e7576e79b88c16d7ee3bbd112c8d90070832885",
|
"rev": "6e34e97ed9788b17796ee43ccdbaf871a5c2b476",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -365,7 +370,8 @@
|
||||||
"hyprland-protocols": "hyprland-protocols",
|
"hyprland-protocols": "hyprland-protocols",
|
||||||
"hyprlang": "hyprlang",
|
"hyprlang": "hyprlang",
|
||||||
"hyprutils": "hyprutils",
|
"hyprutils": "hyprutils",
|
||||||
"hyprwayland-scanner": "hyprwayland-scanner_2",
|
"hyprwayland-scanner": "hyprwayland-scanner",
|
||||||
|
"hyprwire": "hyprwire",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pre-commit-hooks": "pre-commit-hooks",
|
"pre-commit-hooks": "pre-commit-hooks",
|
||||||
"systems": "systems",
|
"systems": "systems",
|
||||||
|
|
|
||||||
76
flake.nix
76
flake.nix
|
|
@ -43,6 +43,7 @@
|
||||||
inputs.hyprgraphics.follows = "hyprgraphics";
|
inputs.hyprgraphics.follows = "hyprgraphics";
|
||||||
inputs.hyprutils.follows = "hyprutils";
|
inputs.hyprutils.follows = "hyprutils";
|
||||||
inputs.hyprlang.follows = "hyprlang";
|
inputs.hyprlang.follows = "hyprlang";
|
||||||
|
inputs.hyprwayland-scanner.follows = "hyprwayland-scanner";
|
||||||
};
|
};
|
||||||
|
|
||||||
hyprlang = {
|
hyprlang = {
|
||||||
|
|
@ -64,6 +65,13 @@
|
||||||
inputs.systems.follows = "systems";
|
inputs.systems.follows = "systems";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hyprwire = {
|
||||||
|
url = "github:hyprwm/hyprwire";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.systems.follows = "systems";
|
||||||
|
inputs.hyprutils.follows = "hyprutils";
|
||||||
|
};
|
||||||
|
|
||||||
xdph = {
|
xdph = {
|
||||||
url = "github:hyprwm/xdg-desktop-portal-hyprland";
|
url = "github:hyprwm/xdg-desktop-portal-hyprland";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
@ -80,23 +88,28 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = inputs @ {
|
outputs =
|
||||||
|
inputs@{
|
||||||
self,
|
self,
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
systems,
|
systems,
|
||||||
...
|
...
|
||||||
}: let
|
}:
|
||||||
|
let
|
||||||
inherit (nixpkgs) lib;
|
inherit (nixpkgs) lib;
|
||||||
eachSystem = lib.genAttrs (import systems);
|
eachSystem = lib.genAttrs (import systems);
|
||||||
pkgsFor = eachSystem (system:
|
pkgsFor = eachSystem (
|
||||||
|
system:
|
||||||
import nixpkgs {
|
import nixpkgs {
|
||||||
localSystem = system;
|
localSystem = system;
|
||||||
overlays = with self.overlays; [
|
overlays = with self.overlays; [
|
||||||
hyprland-packages
|
hyprland-packages
|
||||||
hyprland-extras
|
hyprland-extras
|
||||||
];
|
];
|
||||||
});
|
}
|
||||||
pkgsCrossFor = eachSystem (system: crossSystem:
|
);
|
||||||
|
pkgsCrossFor = eachSystem (
|
||||||
|
system: crossSystem:
|
||||||
import nixpkgs {
|
import nixpkgs {
|
||||||
localSystem = system;
|
localSystem = system;
|
||||||
inherit crossSystem;
|
inherit crossSystem;
|
||||||
|
|
@ -104,29 +117,36 @@
|
||||||
hyprland-packages
|
hyprland-packages
|
||||||
hyprland-extras
|
hyprland-extras
|
||||||
];
|
];
|
||||||
});
|
}
|
||||||
pkgsDebugFor = eachSystem (system:
|
);
|
||||||
|
pkgsDebugFor = eachSystem (
|
||||||
|
system:
|
||||||
import nixpkgs {
|
import nixpkgs {
|
||||||
localSystem = system;
|
localSystem = system;
|
||||||
overlays = with self.overlays; [
|
overlays = with self.overlays; [
|
||||||
hyprland-debug
|
hyprland-debug
|
||||||
];
|
];
|
||||||
});
|
}
|
||||||
pkgsDebugCrossFor = eachSystem (system: crossSystem:
|
);
|
||||||
|
pkgsDebugCrossFor = eachSystem (
|
||||||
|
system: crossSystem:
|
||||||
import nixpkgs {
|
import nixpkgs {
|
||||||
localSystem = system;
|
localSystem = system;
|
||||||
inherit crossSystem;
|
inherit crossSystem;
|
||||||
overlays = with self.overlays; [
|
overlays = with self.overlays; [
|
||||||
hyprland-debug
|
hyprland-debug
|
||||||
];
|
];
|
||||||
});
|
}
|
||||||
in {
|
);
|
||||||
overlays = import ./nix/overlays.nix {inherit self lib inputs;};
|
in
|
||||||
|
{
|
||||||
|
overlays = import ./nix/overlays.nix { inherit self lib inputs; };
|
||||||
|
|
||||||
checks = eachSystem (system:
|
checks = eachSystem (
|
||||||
(lib.filterAttrs
|
system:
|
||||||
(n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n))
|
(lib.filterAttrs (
|
||||||
self.packages.${system})
|
n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n)
|
||||||
|
) self.packages.${system})
|
||||||
// {
|
// {
|
||||||
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
|
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
|
||||||
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
|
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
|
||||||
|
|
@ -136,22 +156,22 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
|
entry = "${self.formatter.${system}}/bin/hyprland-treewide-formatter";
|
||||||
pass_filenames = false;
|
pass_filenames = false;
|
||||||
excludes = ["subprojects"];
|
excludes = [ "subprojects" ];
|
||||||
always_run = true;
|
always_run = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// (import ./nix/tests inputs pkgsFor.${system}));
|
// (import ./nix/tests inputs pkgsFor.${system})
|
||||||
|
);
|
||||||
|
|
||||||
packages = eachSystem (system: {
|
packages = eachSystem (system: {
|
||||||
default = self.packages.${system}.hyprland;
|
default = self.packages.${system}.hyprland;
|
||||||
inherit
|
inherit (pkgsFor.${system})
|
||||||
(pkgsFor.${system})
|
|
||||||
# hyprland-packages
|
# hyprland-packages
|
||||||
hyprland
|
hyprland
|
||||||
hyprland-with-hyprtester
|
|
||||||
hyprland-unwrapped
|
hyprland-unwrapped
|
||||||
|
hyprland-with-tests
|
||||||
# hyprland-extras
|
# hyprland-extras
|
||||||
xdg-desktop-portal-hyprland
|
xdg-desktop-portal-hyprland
|
||||||
;
|
;
|
||||||
|
|
@ -162,18 +182,20 @@
|
||||||
|
|
||||||
devShells = eachSystem (system: {
|
devShells = eachSystem (system: {
|
||||||
default =
|
default =
|
||||||
pkgsFor.${system}.mkShell.override {
|
pkgsFor.${system}.mkShell.override
|
||||||
|
{
|
||||||
inherit (self.packages.${system}.default) stdenv;
|
inherit (self.packages.${system}.default) stdenv;
|
||||||
} {
|
}
|
||||||
|
{
|
||||||
name = "hyprland-shell";
|
name = "hyprland-shell";
|
||||||
hardeningDisable = ["fortify"];
|
hardeningDisable = [ "fortify" ];
|
||||||
inputsFrom = [pkgsFor.${system}.hyprland];
|
inputsFrom = [ pkgsFor.${system}.hyprland ];
|
||||||
packages = [pkgsFor.${system}.clang-tools];
|
packages = [ pkgsFor.${system}.clang-tools ];
|
||||||
inherit (self.checks.${system}.pre-commit-check) shellHook;
|
inherit (self.checks.${system}.pre-commit-check) shellHook;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix {});
|
formatter = eachSystem (system: pkgsFor.${system}.callPackage ./nix/formatter.nix { });
|
||||||
|
|
||||||
nixosModules.default = import ./nix/module.nix inputs;
|
nixosModules.default = import ./nix/module.nix inputs;
|
||||||
homeManagerModules.default = import ./nix/hm-module.nix self;
|
homeManagerModules.default = import ./nix/hm-module.nix self;
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,32 @@ project(
|
||||||
DESCRIPTION "Control utility for Hyprland"
|
DESCRIPTION "Control utility for Hyprland"
|
||||||
)
|
)
|
||||||
|
|
||||||
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 re2)
|
pkg_check_modules(hyprctl_deps REQUIRED IMPORTED_TARGET hyprutils>=0.2.4 hyprwire re2)
|
||||||
|
|
||||||
add_executable(hyprctl "main.cpp")
|
file(GLOB_RECURSE HYPRCTL_SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "hw-protocols/*.cpp" "include/*.hpp")
|
||||||
|
|
||||||
|
add_executable(hyprctl ${HYPRCTL_SRCFILES})
|
||||||
|
|
||||||
target_link_libraries(hyprctl PUBLIC PkgConfig::hyprctl_deps)
|
target_link_libraries(hyprctl PUBLIC PkgConfig::hyprctl_deps)
|
||||||
|
target_include_directories(hyprctl PRIVATE "hw-protocols")
|
||||||
|
|
||||||
|
# Hyprwire
|
||||||
|
|
||||||
|
function(hyprprotocol protoPath protoName)
|
||||||
|
set(path ${CMAKE_CURRENT_SOURCE_DIR}/${protoPath})
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-client.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/${protoName}-spec.hpp
|
||||||
|
COMMAND hyprwire-scanner --client ${path}/${protoName}.xml
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/hw-protocols/
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_sources(hyprctl PRIVATE hw-protocols/${protoName}-client.cpp
|
||||||
|
hw-protocols/${protoName}-client.hpp
|
||||||
|
hw-protocols/${protoName}-spec.hpp)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
hyprprotocol(hw-protocols hyprpaper_core)
|
||||||
|
|
||||||
# binary
|
# binary
|
||||||
install(TARGETS hyprctl)
|
install(TARGETS hyprctl)
|
||||||
|
|
|
||||||
172
hyprctl/hw-protocols/hyprpaper_core.xml
Normal file
172
hyprctl/hw-protocols/hyprpaper_core.xml
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="hyprpaper_core" version="2">
|
||||||
|
<copyright>
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2025, Hypr Development
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<object name="hyprpaper_core_manager" version="2">
|
||||||
|
<description summary="manager object">
|
||||||
|
This is the core manager object for hyprpaper operations
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<c2s name="get_wallpaper_object">
|
||||||
|
<description summary="Get a wallpaper object">
|
||||||
|
Creates a wallpaper object
|
||||||
|
</description>
|
||||||
|
<returns iface="hyprpaper_wallpaper"/>
|
||||||
|
</c2s>
|
||||||
|
|
||||||
|
<s2c name="add_monitor">
|
||||||
|
<description summary="New monitor added">
|
||||||
|
Emitted when a new monitor is added.
|
||||||
|
</description>
|
||||||
|
<arg name="monitor_name" type="varchar" summary="the monitor's name"/>
|
||||||
|
</s2c>
|
||||||
|
|
||||||
|
<s2c name="remove_monitor">
|
||||||
|
<description summary="A monitor was removed">
|
||||||
|
Emitted when a monitor is removed.
|
||||||
|
</description>
|
||||||
|
<arg name="monitor_name" type="varchar" summary="the monitor's name"/>
|
||||||
|
</s2c>
|
||||||
|
|
||||||
|
<c2s name="destroy" destructor="true">
|
||||||
|
<description summary="Destroy this object">
|
||||||
|
Destroys this object. Children remain alive until destroyed.
|
||||||
|
</description>
|
||||||
|
</c2s>
|
||||||
|
|
||||||
|
<c2s name="get_status_object" since="2">
|
||||||
|
<description summary="Get a status object">
|
||||||
|
Creates a status object
|
||||||
|
</description>
|
||||||
|
<returns iface="hyprpaper_status"/>
|
||||||
|
</c2s>
|
||||||
|
</object>
|
||||||
|
|
||||||
|
<enum name="wallpaper_fit_mode">
|
||||||
|
<value idx="0" name="stretch"/>
|
||||||
|
<value idx="1" name="cover"/>
|
||||||
|
<value idx="2" name="contain"/>
|
||||||
|
<value idx="3" name="tile"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="wallpaper_errors">
|
||||||
|
<value idx="0" name="inert_wallpaper_object" description="attempted to use an inert wallpaper object"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="applying_error">
|
||||||
|
<value idx="0" name="invalid_path" description="path provided was invalid"/>
|
||||||
|
<value idx="1" name="invalid_monitor" description="monitor provided was invalid"/>
|
||||||
|
<value idx="2" name="unknown_error" description="unknown error"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<object name="hyprpaper_wallpaper" version="1">
|
||||||
|
<description summary="wallpaper object">
|
||||||
|
This is an object describing a wallpaper
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<c2s name="path">
|
||||||
|
<description summary="Set a path">
|
||||||
|
Set a file path for the wallpaper. This has to be an absolute path from the fs root.
|
||||||
|
This is required.
|
||||||
|
</description>
|
||||||
|
<arg name="wallpaper" type="varchar" summary="path"/>
|
||||||
|
</c2s>
|
||||||
|
|
||||||
|
<c2s name="fit_mode">
|
||||||
|
<description summary="Set a fit mode">
|
||||||
|
Set a fit mode for the wallpaper. This is set to cover by default.
|
||||||
|
</description>
|
||||||
|
<arg name="fit_mode" type="enum" interface="wallpaper_fit_mode" summary="path"/>
|
||||||
|
</c2s>
|
||||||
|
|
||||||
|
<c2s name="monitor_name">
|
||||||
|
<description summary="Set the monitor name">
|
||||||
|
Set a monitor for the wallpaper. Setting this to empty (or not setting at all) will
|
||||||
|
treat this as a wildcard fallback.
|
||||||
|
|
||||||
|
See hyprpaper_core_manager.add_monitor and hyprpaper_core_manager.remove_monitor
|
||||||
|
for tracking monitor names.
|
||||||
|
</description>
|
||||||
|
<arg name="monitor_name" type="varchar" summary="monitor name"/>
|
||||||
|
</c2s>
|
||||||
|
|
||||||
|
<c2s name="apply">
|
||||||
|
<description summary="Apply this wallpaper">
|
||||||
|
Applies this object's state to the wallpaper state. Will emit .success on success,
|
||||||
|
and .failed on failure.
|
||||||
|
|
||||||
|
This object becomes inert after .succeess or .failed, the only valid operation
|
||||||
|
is to destroy it afterwards.
|
||||||
|
</description>
|
||||||
|
</c2s>
|
||||||
|
|
||||||
|
<s2c name="success">
|
||||||
|
<description summary="Operation succeeded">
|
||||||
|
Wallpaper was applied successfully.
|
||||||
|
</description>
|
||||||
|
</s2c>
|
||||||
|
|
||||||
|
<s2c name="failed">
|
||||||
|
<description summary="Operation failed">
|
||||||
|
Wallpaper was not applied. See the error field for more information.
|
||||||
|
</description>
|
||||||
|
<arg name="error" type="enum" interface="hyprpaper_wallpaper_application_error" summary="path"/>
|
||||||
|
</s2c>
|
||||||
|
|
||||||
|
<c2s name="destroy" destructor="true">
|
||||||
|
<description summary="Destroy this object">
|
||||||
|
Destroys this object.
|
||||||
|
</description>
|
||||||
|
</c2s>
|
||||||
|
</object>
|
||||||
|
|
||||||
|
<object name="hyprpaper_status" version="2">
|
||||||
|
<description summary="status object">
|
||||||
|
This is an object which will emit various status updates.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<s2c name="active_wallpaper">
|
||||||
|
<description summary="Active wallpaper state">
|
||||||
|
Sends the active wallpaper for a given monitor. This will be emitted
|
||||||
|
immediately after binding, and then every time the path changes.
|
||||||
|
</description>
|
||||||
|
<arg name="monitor" type="varchar" summary="monitor name"/>
|
||||||
|
<arg name="path" type="varchar" summary="wallpaper path"/>
|
||||||
|
</s2c>
|
||||||
|
|
||||||
|
<c2s name="destroy" destructor="true">
|
||||||
|
<description summary="Destroy this object">
|
||||||
|
Destroys this object.
|
||||||
|
</description>
|
||||||
|
</c2s>
|
||||||
|
</object>
|
||||||
|
</protocol>
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
executable(
|
|
||||||
'hyprctl',
|
|
||||||
'main.cpp',
|
|
||||||
dependencies: [
|
|
||||||
dependency('hyprutils', version: '>= 0.1.1'),
|
|
||||||
dependency('re2', required: true)
|
|
||||||
],
|
|
||||||
install: true,
|
|
||||||
)
|
|
||||||
|
|
||||||
install_data(
|
|
||||||
'hyprctl.bash',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
rename: 'hyprctl',
|
|
||||||
)
|
|
||||||
install_data(
|
|
||||||
'hyprctl.fish',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
)
|
|
||||||
install_data(
|
|
||||||
'hyprctl.zsh',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
rename: '_hyprctl',
|
|
||||||
)
|
|
||||||
|
|
@ -74,11 +74,8 @@ flags:
|
||||||
const std::string_view HYPRPAPER_HELP = R"#(usage: hyprctl [flags] hyprpaper <request>
|
const std::string_view HYPRPAPER_HELP = R"#(usage: hyprctl [flags] hyprpaper <request>
|
||||||
|
|
||||||
requests:
|
requests:
|
||||||
listactive → Lists all active images
|
wallpaper → Issue a wallpaper to call a config wallpaper dynamically.
|
||||||
listloaded → Lists all loaded images
|
Arguments are [mon],[path],[fit_mode]. Fit mode is optional.
|
||||||
preload <path> → Preloads image
|
|
||||||
unload <path> → Unloads image. Pass 'all' as path to unload all images
|
|
||||||
wallpaper → Issue a wallpaper to call a config wallpaper dynamically
|
|
||||||
|
|
||||||
flags:
|
flags:
|
||||||
See 'hyprctl --help')#";
|
See 'hyprctl --help')#";
|
||||||
11
hyprctl/src/helpers/Memory.hpp
Normal file
11
hyprctl/src/helpers/Memory.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hyprutils/memory/SharedPtr.hpp>
|
||||||
|
#include <hyprutils/memory/UniquePtr.hpp>
|
||||||
|
#include <hyprutils/memory/Atomic.hpp>
|
||||||
|
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
#define SP CSharedPointer
|
||||||
|
#define WP CWeakPointer
|
||||||
|
#define UP CUniquePointer
|
||||||
208
hyprctl/src/hyprpaper/Hyprpaper.cpp
Normal file
208
hyprctl/src/hyprpaper/Hyprpaper.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
#include "Hyprpaper.hpp"
|
||||||
|
#include "../helpers/Memory.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <format>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <print>
|
||||||
|
|
||||||
|
#include <hyprpaper_core-client.hpp>
|
||||||
|
|
||||||
|
#include <hyprutils/string/VarList2.hpp>
|
||||||
|
using namespace Hyprutils::String;
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
constexpr const char* SOCKET_NAME = ".hyprpaper.sock";
|
||||||
|
static SP<CCHyprpaperCoreImpl> g_coreImpl;
|
||||||
|
|
||||||
|
constexpr const uint32_t PROTOCOL_VERSION_SUPPORTED = 2;
|
||||||
|
|
||||||
|
//
|
||||||
|
static hyprpaperCoreWallpaperFitMode fitFromString(const std::string_view& sv) {
|
||||||
|
if (sv == "contain")
|
||||||
|
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_CONTAIN;
|
||||||
|
if (sv == "fit" || sv == "stretch")
|
||||||
|
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_STRETCH;
|
||||||
|
if (sv == "tile")
|
||||||
|
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_TILE;
|
||||||
|
return HYPRPAPER_CORE_WALLPAPER_FIT_MODE_COVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::expected<std::string, std::string> resolvePath(const std::string_view& sv) {
|
||||||
|
std::error_code ec;
|
||||||
|
auto can = std::filesystem::canonical(sv, ec);
|
||||||
|
|
||||||
|
if (ec)
|
||||||
|
return std::unexpected(std::format("invalid path: {}", ec.message()));
|
||||||
|
|
||||||
|
return can;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::expected<std::string, std::string> getFullPath(const std::string_view& sv) {
|
||||||
|
if (sv.empty())
|
||||||
|
return std::unexpected("empty path");
|
||||||
|
|
||||||
|
if (sv[0] == '~') {
|
||||||
|
static auto HOME = getenv("HOME");
|
||||||
|
if (!HOME || HOME[0] == '\0')
|
||||||
|
return std::unexpected("home path but no $HOME");
|
||||||
|
|
||||||
|
return resolvePath(std::string{HOME} + "/"s + std::string{sv.substr(1)});
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvePath(sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::expected<void, std::string> doWallpaper(const std::string_view& RHS) {
|
||||||
|
CVarList2 args(std::string{RHS}, 0, ',');
|
||||||
|
|
||||||
|
const std::string MONITOR = std::string{args[0]};
|
||||||
|
const auto& PATH_RAW = args[1];
|
||||||
|
const auto& FIT = args[2];
|
||||||
|
|
||||||
|
if (PATH_RAW.empty())
|
||||||
|
return std::unexpected("not enough args");
|
||||||
|
|
||||||
|
const auto RTDIR = getenv("XDG_RUNTIME_DIR");
|
||||||
|
|
||||||
|
if (!RTDIR || RTDIR[0] == '\0')
|
||||||
|
return std::unexpected("can't send: no XDG_RUNTIME_DIR");
|
||||||
|
|
||||||
|
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
|
if (!HIS || HIS[0] == '\0')
|
||||||
|
return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)");
|
||||||
|
|
||||||
|
const auto PATH = getFullPath(PATH_RAW);
|
||||||
|
|
||||||
|
if (!PATH)
|
||||||
|
return std::unexpected(std::format("bad path: {}", PATH_RAW));
|
||||||
|
|
||||||
|
auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME;
|
||||||
|
|
||||||
|
auto socket = Hyprwire::IClientSocket::open(socketPath);
|
||||||
|
|
||||||
|
if (!socket)
|
||||||
|
return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)");
|
||||||
|
|
||||||
|
g_coreImpl = makeShared<CCHyprpaperCoreImpl>(PROTOCOL_VERSION_SUPPORTED);
|
||||||
|
|
||||||
|
socket->addImplementation(g_coreImpl);
|
||||||
|
|
||||||
|
if (!socket->waitForHandshake())
|
||||||
|
return std::unexpected("can't send: wire handshake failed");
|
||||||
|
|
||||||
|
auto spec = socket->getSpec(g_coreImpl->protocol()->specName());
|
||||||
|
|
||||||
|
if (!spec)
|
||||||
|
return std::unexpected("can't send: hyprpaper doesn't have the spec?!");
|
||||||
|
|
||||||
|
auto manager = makeShared<CCHyprpaperCoreManagerObject>(socket->bindProtocol(g_coreImpl->protocol(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer())));
|
||||||
|
|
||||||
|
if (!manager)
|
||||||
|
return std::unexpected("wire error: couldn't create manager");
|
||||||
|
|
||||||
|
auto wallpaper = makeShared<CCHyprpaperWallpaperObject>(manager->sendGetWallpaperObject());
|
||||||
|
|
||||||
|
if (!wallpaper)
|
||||||
|
return std::unexpected("wire error: couldn't create wallpaper object");
|
||||||
|
|
||||||
|
bool canExit = false;
|
||||||
|
std::optional<std::string> err;
|
||||||
|
|
||||||
|
wallpaper->setFailed([&canExit, &err](uint32_t code) {
|
||||||
|
canExit = true;
|
||||||
|
switch (code) {
|
||||||
|
case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_PATH: err = std::format("failed to set wallpaper: Invalid path", code); break;
|
||||||
|
case HYPRPAPER_CORE_APPLYING_ERROR_INVALID_MONITOR: err = std::format("failed to set wallpaper: Invalid monitor", code); break;
|
||||||
|
default: err = std::format("failed to set wallpaper: unknown error, code {}", code); break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wallpaper->setSuccess([&canExit]() { canExit = true; });
|
||||||
|
|
||||||
|
wallpaper->sendPath(PATH->c_str());
|
||||||
|
wallpaper->sendMonitorName(MONITOR.c_str());
|
||||||
|
if (!FIT.empty())
|
||||||
|
wallpaper->sendFitMode(fitFromString(FIT));
|
||||||
|
|
||||||
|
wallpaper->sendApply();
|
||||||
|
|
||||||
|
while (!canExit) {
|
||||||
|
socket->dispatchEvents(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return std::unexpected(*err);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::expected<void, std::string> doListActive() {
|
||||||
|
const auto RTDIR = getenv("XDG_RUNTIME_DIR");
|
||||||
|
|
||||||
|
if (!RTDIR || RTDIR[0] == '\0')
|
||||||
|
return std::unexpected("can't send: no XDG_RUNTIME_DIR");
|
||||||
|
|
||||||
|
const auto HIS = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
|
if (!HIS || HIS[0] == '\0')
|
||||||
|
return std::unexpected("can't send: no HYPRLAND_INSTANCE_SIGNATURE (not running under hyprland)");
|
||||||
|
|
||||||
|
auto socketPath = RTDIR + "/hypr/"s + HIS + "/"s + SOCKET_NAME;
|
||||||
|
|
||||||
|
auto socket = Hyprwire::IClientSocket::open(socketPath);
|
||||||
|
|
||||||
|
if (!socket)
|
||||||
|
return std::unexpected("can't send: failed to connect to hyprpaper (is it running?)");
|
||||||
|
|
||||||
|
g_coreImpl = makeShared<CCHyprpaperCoreImpl>(PROTOCOL_VERSION_SUPPORTED);
|
||||||
|
|
||||||
|
socket->addImplementation(g_coreImpl);
|
||||||
|
|
||||||
|
if (!socket->waitForHandshake())
|
||||||
|
return std::unexpected("can't send: wire handshake failed");
|
||||||
|
|
||||||
|
auto spec = socket->getSpec(g_coreImpl->protocol()->specName());
|
||||||
|
|
||||||
|
if (!spec)
|
||||||
|
return std::unexpected("can't send: hyprpaper doesn't have the spec?!");
|
||||||
|
|
||||||
|
if (spec->specVer() < 2)
|
||||||
|
return std::unexpected("can't send: hyprpaper protocol version too low (hyprpaper too old)");
|
||||||
|
|
||||||
|
auto manager = makeShared<CCHyprpaperCoreManagerObject>(socket->bindProtocol(g_coreImpl->protocol(), std::min(PROTOCOL_VERSION_SUPPORTED, spec->specVer())));
|
||||||
|
|
||||||
|
if (!manager)
|
||||||
|
return std::unexpected("wire error: couldn't create manager");
|
||||||
|
|
||||||
|
auto status = makeShared<CCHyprpaperStatusObject>(manager->sendGetStatusObject());
|
||||||
|
|
||||||
|
status->setActiveWallpaper([](const char* mon, const char* wp) { std::println("{}: {}", mon, wp); });
|
||||||
|
|
||||||
|
socket->roundtrip();
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, std::string> Hyprpaper::makeHyprpaperRequest(const std::string_view& rq) {
|
||||||
|
if (!rq.contains(' '))
|
||||||
|
return std::unexpected("Invalid request");
|
||||||
|
|
||||||
|
if (!rq.starts_with("/hyprpaper "))
|
||||||
|
return std::unexpected("Invalid request");
|
||||||
|
|
||||||
|
std::string_view LHS, RHS;
|
||||||
|
auto spacePos = rq.find(' ', 12);
|
||||||
|
LHS = rq.substr(11, spacePos - 11);
|
||||||
|
RHS = rq.substr(spacePos + 1);
|
||||||
|
|
||||||
|
if (LHS == "wallpaper")
|
||||||
|
return doWallpaper(RHS);
|
||||||
|
else if (LHS == "listactive")
|
||||||
|
return doListActive();
|
||||||
|
else
|
||||||
|
return std::unexpected("invalid hyprpaper request");
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
8
hyprctl/src/hyprpaper/Hyprpaper.hpp
Normal file
8
hyprctl/src/hyprpaper/Hyprpaper.hpp
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <expected>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Hyprpaper {
|
||||||
|
std::expected<void, std::string> makeHyprpaperRequest(const std::string_view& rq);
|
||||||
|
};
|
||||||
|
|
@ -31,6 +31,7 @@ using namespace Hyprutils::String;
|
||||||
using namespace Hyprutils::Memory;
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
#include "Strings.hpp"
|
#include "Strings.hpp"
|
||||||
|
#include "hyprpaper/Hyprpaper.hpp"
|
||||||
|
|
||||||
std::string instanceSignature;
|
std::string instanceSignature;
|
||||||
bool quiet = false;
|
bool quiet = false;
|
||||||
|
|
@ -227,6 +228,9 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
|
||||||
constexpr size_t BUFFER_SIZE = 8192;
|
constexpr size_t BUFFER_SIZE = 8192;
|
||||||
char buffer[BUFFER_SIZE] = {0};
|
char buffer[BUFFER_SIZE] = {0};
|
||||||
|
|
||||||
|
// read all data until server closes the connection
|
||||||
|
// this handles partial writes on the server side under high load
|
||||||
|
while (true) {
|
||||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
||||||
|
|
||||||
if (sizeWritten < 0) {
|
if (sizeWritten < 0) {
|
||||||
|
|
@ -236,14 +240,11 @@ int request(std::string_view arg, int minArgs = 0, bool needRoll = false) {
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
reply += std::string(buffer, sizeWritten);
|
if (sizeWritten == 0) {
|
||||||
|
// server closed connection, we're done
|
||||||
while (sizeWritten == BUFFER_SIZE) {
|
break;
|
||||||
sizeWritten = read(SERVERSOCKET, buffer, BUFFER_SIZE);
|
|
||||||
if (sizeWritten < 0) {
|
|
||||||
log("Couldn't read (6)");
|
|
||||||
return 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reply += std::string(buffer, sizeWritten);
|
reply += std::string(buffer, sizeWritten);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,10 +306,6 @@ int requestIPC(std::string_view filename, std::string_view arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int requestHyprpaper(std::string_view arg) {
|
|
||||||
return requestIPC(".hyprpaper.sock", arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int requestHyprsunset(std::string_view arg) {
|
int requestHyprsunset(std::string_view arg) {
|
||||||
return requestIPC(".hyprsunset.sock", arg);
|
return requestIPC(".hyprsunset.sock", arg);
|
||||||
}
|
}
|
||||||
|
|
@ -500,9 +497,12 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
if (fullRequest.contains("/--batch"))
|
if (fullRequest.contains("/--batch"))
|
||||||
batchRequest(fullRequest, json);
|
batchRequest(fullRequest, json);
|
||||||
else if (fullRequest.contains("/hyprpaper"))
|
else if (fullRequest.contains("/hyprpaper")) {
|
||||||
exitStatus = requestHyprpaper(fullRequest);
|
auto result = Hyprpaper::makeHyprpaperRequest(fullRequest);
|
||||||
else if (fullRequest.contains("/hyprsunset"))
|
if (!result)
|
||||||
|
log(std::format("error: {}", result.error()));
|
||||||
|
exitStatus = !result;
|
||||||
|
} else if (fullRequest.contains("/hyprsunset"))
|
||||||
exitStatus = requestHyprsunset(fullRequest);
|
exitStatus = requestHyprsunset(fullRequest);
|
||||||
else if (fullRequest.contains("/switchxkblayout"))
|
else if (fullRequest.contains("/switchxkblayout"))
|
||||||
exitStatus = request(fullRequest, 2);
|
exitStatus = request(fullRequest, 2);
|
||||||
|
|
@ -4,4 +4,5 @@ Name: Hyprland
|
||||||
URL: https://github.com/hyprwm/Hyprland
|
URL: https://github.com/hyprwm/Hyprland
|
||||||
Description: Hyprland header files
|
Description: Hyprland header files
|
||||||
Version: @HYPRLAND_VERSION@
|
Version: @HYPRLAND_VERSION@
|
||||||
|
Requires: aquamarine >= @AQUAMARINE_MINIMUM_VERSION@, hyprcursor >= @HYPRCURSOR_MINIMUM_VERSION@, hyprgraphics >= @HYPRGRAPHICS_MINIMUM_VERSION@, hyprlang >= @HYPRLANG_MINIMUM_VERSION@, hyprutils >= @HYPRUTILS_MINIMUM_VERSION@, libdrm, egl, cairo, xkbcommon >= @XKBCOMMON_MINIMUM_VERSION@, libinput >= @LIBINPUT_MINIMUM_VERSION@, wayland-server >= @WAYLAND_SERVER_MINIMUM_VERSION@@PKGCONFIG_XWAYLAND_DEPENDENCIES@
|
||||||
Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland
|
Cflags: -I${prefix} -I${prefix}/hyprland/protocols -I${prefix}/hyprland
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
|
pkg_check_modules(hyprpm_deps REQUIRED IMPORTED_TARGET tomlplusplus hyprutils>=0.7.0)
|
||||||
|
|
||||||
find_package(glaze QUIET)
|
find_package(glaze 6.0.1 QUIET)
|
||||||
if (NOT glaze_FOUND)
|
if (NOT glaze_FOUND)
|
||||||
set(GLAZE_VERSION v5.1.1)
|
set(GLAZE_VERSION v6.0.1)
|
||||||
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
message(STATUS "glaze dependency not found, retrieving ${GLAZE_VERSION} with FetchContent")
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
|
|
@ -21,6 +21,7 @@ if (NOT glaze_FOUND)
|
||||||
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
|
GIT_REPOSITORY https://github.com/stephenberry/glaze.git
|
||||||
GIT_TAG ${GLAZE_VERSION}
|
GIT_TAG ${GLAZE_VERSION}
|
||||||
GIT_SHALLOW TRUE
|
GIT_SHALLOW TRUE
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(glaze)
|
FetchContent_MakeAvailable(glaze)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||||
auto DATA = toml::table{
|
auto DATA = toml::table{
|
||||||
{"repository", toml::table{
|
{"repository", toml::table{
|
||||||
{"name", repo.name},
|
{"name", repo.name},
|
||||||
|
{"author", repo.author},
|
||||||
{"hash", repo.hash},
|
{"hash", repo.hash},
|
||||||
{"url", repo.url},
|
{"url", repo.url},
|
||||||
{"rev", repo.rev}
|
{"rev", repo.rev}
|
||||||
|
|
@ -122,31 +123,32 @@ void DataState::addNewPluginRepo(const SPluginRepository& repo) {
|
||||||
Debug::die("{}", failureString("Failed to write plugin state"));
|
Debug::die("{}", failureString("Failed to write plugin state"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DataState::pluginRepoExists(const std::string& urlOrName) {
|
bool DataState::pluginRepoExists(const SPluginRepoIdentifier identifier) {
|
||||||
ensureStateStoreExists();
|
ensureStateStoreExists();
|
||||||
|
|
||||||
for (const auto& stateFile : getPluginStates()) {
|
for (const auto& stateFile : getPluginStates()) {
|
||||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||||
|
const auto AUTHOR = STATE["repository"]["author"].value_or("");
|
||||||
const auto URL = STATE["repository"]["url"].value_or("");
|
const auto URL = STATE["repository"]["url"].value_or("");
|
||||||
|
|
||||||
if (URL == urlOrName || NAME == urlOrName)
|
if (identifier.matches(URL, NAME, AUTHOR))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataState::removePluginRepo(const std::string& urlOrName) {
|
void DataState::removePluginRepo(const SPluginRepoIdentifier identifier) {
|
||||||
ensureStateStoreExists();
|
ensureStateStoreExists();
|
||||||
|
|
||||||
for (const auto& stateFile : getPluginStates()) {
|
for (const auto& stateFile : getPluginStates()) {
|
||||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||||
|
const auto AUTHOR = STATE["repository"]["author"].value_or("");
|
||||||
const auto URL = STATE["repository"]["url"].value_or("");
|
const auto URL = STATE["repository"]["url"].value_or("");
|
||||||
|
|
||||||
if (URL == urlOrName || NAME == urlOrName) {
|
if (identifier.matches(URL, NAME, AUTHOR)) {
|
||||||
|
|
||||||
// unload the plugins!!
|
// unload the plugins!!
|
||||||
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
|
for (const auto& file : std::filesystem::directory_iterator(stateFile.parent_path())) {
|
||||||
if (!file.path().string().ends_with(".so"))
|
if (!file.path().string().ends_with(".so"))
|
||||||
|
|
@ -181,7 +183,7 @@ void DataState::updateGlobalState(const SGlobalState& state) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
auto DATA = toml::table{
|
auto DATA = toml::table{
|
||||||
{"state", toml::table{
|
{"state", toml::table{
|
||||||
{"hash", state.headersHashCompiled},
|
{"hash", state.headersAbiCompiled},
|
||||||
{"dont_warn_install", state.dontWarnInstall}
|
{"dont_warn_install", state.dontWarnInstall}
|
||||||
}}
|
}}
|
||||||
};
|
};
|
||||||
|
|
@ -206,7 +208,7 @@ SGlobalState DataState::getGlobalState() {
|
||||||
auto DATA = toml::parse_file(stateFile.c_str());
|
auto DATA = toml::parse_file(stateFile.c_str());
|
||||||
|
|
||||||
SGlobalState state;
|
SGlobalState state;
|
||||||
state.headersHashCompiled = DATA["state"]["hash"].value_or("");
|
state.headersAbiCompiled = DATA["state"]["hash"].value_or("");
|
||||||
state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
|
state.dontWarnInstall = DATA["state"]["dont_warn_install"].value_or(false);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
|
@ -220,6 +222,7 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||||
const auto STATE = toml::parse_file(stateFile.c_str());
|
const auto STATE = toml::parse_file(stateFile.c_str());
|
||||||
|
|
||||||
const auto NAME = STATE["repository"]["name"].value_or("");
|
const auto NAME = STATE["repository"]["name"].value_or("");
|
||||||
|
const auto AUTHOR = STATE["repository"]["author"].value_or("");
|
||||||
const auto URL = STATE["repository"]["url"].value_or("");
|
const auto URL = STATE["repository"]["url"].value_or("");
|
||||||
const auto REV = STATE["repository"]["rev"].value_or("");
|
const auto REV = STATE["repository"]["rev"].value_or("");
|
||||||
const auto HASH = STATE["repository"]["hash"].value_or("");
|
const auto HASH = STATE["repository"]["hash"].value_or("");
|
||||||
|
|
@ -227,6 +230,7 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||||
SPluginRepository repo;
|
SPluginRepository repo;
|
||||||
repo.hash = HASH;
|
repo.hash = HASH;
|
||||||
repo.name = NAME;
|
repo.name = NAME;
|
||||||
|
repo.author = AUTHOR;
|
||||||
repo.url = URL;
|
repo.url = URL;
|
||||||
repo.rev = REV;
|
repo.rev = REV;
|
||||||
|
|
||||||
|
|
@ -247,7 +251,7 @@ std::vector<SPluginRepository> DataState::getAllRepositories() {
|
||||||
return repos;
|
return repos;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
bool DataState::setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled) {
|
||||||
ensureStateStoreExists();
|
ensureStateStoreExists();
|
||||||
|
|
||||||
for (const auto& stateFile : getPluginStates()) {
|
for (const auto& stateFile : getPluginStates()) {
|
||||||
|
|
@ -256,8 +260,17 @@ bool DataState::setPluginEnabled(const std::string& name, bool enabled) {
|
||||||
if (key == "repository")
|
if (key == "repository")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (key.str() != name)
|
switch (identifier.type) {
|
||||||
|
case IDENTIFIER_NAME:
|
||||||
|
if (key.str() != identifier.name)
|
||||||
continue;
|
continue;
|
||||||
|
break;
|
||||||
|
case IDENTIFIER_AUTHOR_NAME:
|
||||||
|
if (STATE["repository"]["author"] != identifier.author || key.str() != identifier.name)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
const auto FAILED = STATE[key]["failed"].value_or(false);
|
const auto FAILED = STATE[key]["failed"].value_or(false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
#include "Plugin.hpp"
|
#include "Plugin.hpp"
|
||||||
|
|
||||||
struct SGlobalState {
|
struct SGlobalState {
|
||||||
std::string headersHashCompiled = "";
|
std::string headersAbiCompiled = "";
|
||||||
bool dontWarnInstall = false;
|
bool dontWarnInstall = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -15,11 +15,11 @@ namespace DataState {
|
||||||
std::vector<std::filesystem::path> getPluginStates();
|
std::vector<std::filesystem::path> getPluginStates();
|
||||||
void ensureStateStoreExists();
|
void ensureStateStoreExists();
|
||||||
void addNewPluginRepo(const SPluginRepository& repo);
|
void addNewPluginRepo(const SPluginRepository& repo);
|
||||||
void removePluginRepo(const std::string& urlOrName);
|
void removePluginRepo(const SPluginRepoIdentifier identifier);
|
||||||
bool pluginRepoExists(const std::string& urlOrName);
|
bool pluginRepoExists(const SPluginRepoIdentifier identifier);
|
||||||
void updateGlobalState(const SGlobalState& state);
|
void updateGlobalState(const SGlobalState& state);
|
||||||
void purgeAllCache();
|
void purgeAllCache();
|
||||||
SGlobalState getGlobalState();
|
SGlobalState getGlobalState();
|
||||||
bool setPluginEnabled(const std::string& name, bool enabled);
|
bool setPluginEnabled(const SPluginRepoIdentifier identifier, bool enabled);
|
||||||
std::vector<SPluginRepository> getAllRepositories();
|
std::vector<SPluginRepository> getAllRepositories();
|
||||||
};
|
};
|
||||||
48
hyprpm/src/core/Plugin.cpp
Normal file
48
hyprpm/src/core/Plugin.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "Plugin.hpp"
|
||||||
|
|
||||||
|
SPluginRepoIdentifier SPluginRepoIdentifier::fromUrl(const std::string& url) {
|
||||||
|
return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = url};
|
||||||
|
}
|
||||||
|
|
||||||
|
SPluginRepoIdentifier SPluginRepoIdentifier::fromName(const std::string& name) {
|
||||||
|
return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = name};
|
||||||
|
}
|
||||||
|
|
||||||
|
SPluginRepoIdentifier SPluginRepoIdentifier::fromAuthorName(const std::string& author, const std::string& name) {
|
||||||
|
return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
|
||||||
|
}
|
||||||
|
|
||||||
|
SPluginRepoIdentifier SPluginRepoIdentifier::fromString(const std::string& string) {
|
||||||
|
if (string.find(':') != std::string::npos) {
|
||||||
|
return SPluginRepoIdentifier{.type = IDENTIFIER_URL, .url = string};
|
||||||
|
} else {
|
||||||
|
auto slashPos = string.find('/');
|
||||||
|
if (slashPos != std::string::npos) {
|
||||||
|
std::string author = string.substr(0, slashPos);
|
||||||
|
std::string name = string.substr(slashPos + 1, string.size() - slashPos - 1);
|
||||||
|
return SPluginRepoIdentifier{.type = IDENTIFIER_AUTHOR_NAME, .name = name, .author = author};
|
||||||
|
} else {
|
||||||
|
return SPluginRepoIdentifier{.type = IDENTIFIER_NAME, .name = string};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SPluginRepoIdentifier::toString() const {
|
||||||
|
switch (type) {
|
||||||
|
case IDENTIFIER_NAME: return name;
|
||||||
|
case IDENTIFIER_AUTHOR_NAME: return author + '/' + name;
|
||||||
|
case IDENTIFIER_URL: return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SPluginRepoIdentifier::matches(const std::string& url, const std::string& name, const std::string& author) const {
|
||||||
|
switch (type) {
|
||||||
|
case IDENTIFIER_URL: return this->url == url;
|
||||||
|
case IDENTIFIER_NAME: return this->name == name;
|
||||||
|
case IDENTIFIER_AUTHOR_NAME: return this->author == author && this->name == name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,27 @@ struct SPluginRepository {
|
||||||
std::string url;
|
std::string url;
|
||||||
std::string rev;
|
std::string rev;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string author;
|
||||||
std::vector<SPlugin> plugins;
|
std::vector<SPlugin> plugins;
|
||||||
std::string hash;
|
std::string hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ePluginRepoIdentifierType {
|
||||||
|
IDENTIFIER_URL,
|
||||||
|
IDENTIFIER_NAME,
|
||||||
|
IDENTIFIER_AUTHOR_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SPluginRepoIdentifier {
|
||||||
|
ePluginRepoIdentifierType type;
|
||||||
|
std::string url = "";
|
||||||
|
std::string name = "";
|
||||||
|
std::string author = "";
|
||||||
|
|
||||||
|
static SPluginRepoIdentifier fromString(const std::string& string);
|
||||||
|
static SPluginRepoIdentifier fromUrl(const std::string& Url);
|
||||||
|
static SPluginRepoIdentifier fromName(const std::string& name);
|
||||||
|
static SPluginRepoIdentifier fromAuthorName(const std::string& author, const std::string& name);
|
||||||
|
std::string toString() const;
|
||||||
|
bool matches(const std::string& url, const std::string& name, const std::string& author) const;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -78,40 +79,33 @@ SHyprlandVersion CPluginManager::getHyprlandVersion(bool running) {
|
||||||
else
|
else
|
||||||
onceInstalled = true;
|
onceInstalled = true;
|
||||||
|
|
||||||
const auto HLVERCALL = running ? NHyprlandSocket::send("/version") : execAndGet("Hyprland --version");
|
const auto HLVERCALL = running ? NHyprlandSocket::send("j/version") : execAndGet("Hyprland --version-json");
|
||||||
if (m_bVerbose)
|
|
||||||
std::println("{}", verboseString("{} version returned: {}", running ? "running" : "installed", HLVERCALL));
|
|
||||||
|
|
||||||
if (!HLVERCALL.contains("Tag:")) {
|
auto jsonQuery = glz::read_json<glz::generic>(HLVERCALL);
|
||||||
std::println(stderr, "\n{}", failureString("You don't seem to be running Hyprland."));
|
|
||||||
|
if (!jsonQuery) {
|
||||||
|
std::println("{}", failureString("failed to get the current hyprland version. Are you running hyprland?"));
|
||||||
return SHyprlandVersion{};
|
return SHyprlandVersion{};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string hlcommit = HLVERCALL.substr(HLVERCALL.find("at commit") + 10);
|
auto hlbranch = (*jsonQuery)["branch"].get_string();
|
||||||
hlcommit = hlcommit.substr(0, hlcommit.find_first_of(' '));
|
auto hlcommit = (*jsonQuery)["commit"].get_string();
|
||||||
|
auto abiHash = (*jsonQuery)["abiHash"].get_string();
|
||||||
|
auto hldate = (*jsonQuery)["commit_date"].get_string();
|
||||||
|
auto hlcommits = (*jsonQuery)["commits"].get_string();
|
||||||
|
|
||||||
std::string hlbranch = HLVERCALL.substr(HLVERCALL.find("from branch") + 12);
|
auto flags = (*jsonQuery)["flags"].get_array();
|
||||||
hlbranch = hlbranch.substr(0, hlbranch.find(" at commit "));
|
bool isNix = std::ranges::any_of(flags, [](const auto& f) { return f.is_string() && f.get_string() == std::string_view{"nix"}; });
|
||||||
|
|
||||||
std::string hldate = HLVERCALL.substr(HLVERCALL.find("Date: ") + 6);
|
size_t commits = 0;
|
||||||
hldate = hldate.substr(0, hldate.find('\n'));
|
|
||||||
|
|
||||||
std::string hlcommits;
|
|
||||||
|
|
||||||
if (HLVERCALL.contains("commits:")) {
|
|
||||||
hlcommits = HLVERCALL.substr(HLVERCALL.find("commits:") + 9);
|
|
||||||
hlcommits = hlcommits.substr(0, hlcommits.find(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
int commits = 0;
|
|
||||||
try {
|
try {
|
||||||
commits = std::stoi(hlcommits);
|
commits = std::stoull(hlcommits);
|
||||||
} catch (...) { ; }
|
} catch (...) { ; }
|
||||||
|
|
||||||
if (m_bVerbose)
|
if (m_bVerbose)
|
||||||
std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}", hlcommit, hlbranch, hldate, commits));
|
std::println("{}", verboseString("parsed commit {} at branch {} on {}, commits {}, nix: {}", hlcommit, hlbranch, hldate, commits, isNix));
|
||||||
|
|
||||||
auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, commits};
|
auto ver = SHyprlandVersion{hlbranch, hlcommit, hldate, abiHash, commits, isNix};
|
||||||
|
|
||||||
if (running)
|
if (running)
|
||||||
verRunning = ver;
|
verRunning = ver;
|
||||||
|
|
@ -137,16 +131,24 @@ bool CPluginManager::createSafeDirectory(const std::string& path) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CPluginManager::validArg(const std::string& s) {
|
||||||
|
return !s.contains("'") && !s.ends_with("\\") && !s.starts_with("\\");
|
||||||
|
}
|
||||||
|
|
||||||
bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) {
|
bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string& rev) {
|
||||||
const auto HLVER = getHyprlandVersion();
|
const auto HLVER = getHyprlandVersion();
|
||||||
|
|
||||||
if (!hasDeps()) {
|
if (!validArg(url) || !validArg(rev)) {
|
||||||
std::println(stderr, "\n{}",
|
std::println(stderr, "\n{}", failureString("url or rev invalid"));
|
||||||
failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config, git, g++, gcc"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DataState::pluginRepoExists(url)) {
|
if (!hasDeps()) {
|
||||||
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DataState::pluginRepoExists(SPluginRepoIdentifier::fromUrl(url))) {
|
||||||
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. Repository already installed."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +163,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||||
DataState::updateGlobalState(GLOBALSTATE);
|
DataState::updateGlobalState(GLOBALSTATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GLOBALSTATE.headersHashCompiled.empty()) {
|
if (GLOBALSTATE.headersAbiCompiled.empty()) {
|
||||||
std::println("\n{}", failureString("Cannot find headers in the global state. Try running hyprpm update first."));
|
std::println("\n{}", failureString("Cannot find headers in the global state. Try running hyprpm update first."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +207,7 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||||
|
|
||||||
progress.printMessageAbove(infoString("Cloning {}", url));
|
progress.printMessageAbove(infoString("Cloning {}", url));
|
||||||
|
|
||||||
std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), url, USERNAME));
|
std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), url, USERNAME));
|
||||||
|
|
||||||
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
||||||
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
|
std::println(stderr, "\n{}", failureString("Could not clone the plugin repository. shell returned:\n{}", ret));
|
||||||
|
|
@ -312,8 +314,14 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||||
progress.printMessageAbove(infoString("Building {}", p.name));
|
progress.printMessageAbove(infoString("Building {}", p.name));
|
||||||
|
|
||||||
for (auto const& bs : p.buildSteps) {
|
for (auto const& bs : p.buildSteps) {
|
||||||
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
|
const auto CMD_RAW = nixDevelopIfNeeded(std::format("cd {} && PKG_CONFIG_PATH=\"{}\" {}", m_szWorkingPluginDirectory, getPkgConfigPath(), bs), HLVER);
|
||||||
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
|
|
||||||
|
if (!CMD_RAW) {
|
||||||
|
progress.printMessageAbove(failureString("Failed to build {}: {}", p.name, CMD_RAW.error()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out += " -> " + *CMD_RAW + "\n" + execAndGet(*CMD_RAW) + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_bVerbose)
|
if (m_bVerbose)
|
||||||
|
|
@ -343,7 +351,10 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||||
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
std::string repohash = execAndGet("cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD");
|
||||||
if (repohash.length() > 0)
|
if (repohash.length() > 0)
|
||||||
repohash.pop_back();
|
repohash.pop_back();
|
||||||
repo.name = pManifest->m_repository.name.empty() ? url.substr(url.find_last_of('/') + 1) : pManifest->m_repository.name;
|
auto lastSlash = url.find_last_of('/');
|
||||||
|
auto secondLastSlash = url.find_last_of('/', lastSlash - 1);
|
||||||
|
repo.name = pManifest->m_repository.name.empty() ? url.substr(lastSlash + 1) : pManifest->m_repository.name;
|
||||||
|
repo.author = url.substr(secondLastSlash + 1, lastSlash - secondLastSlash - 1);
|
||||||
repo.url = url;
|
repo.url = url;
|
||||||
repo.rev = rev;
|
repo.rev = rev;
|
||||||
repo.hash = repohash;
|
repo.hash = repohash;
|
||||||
|
|
@ -366,13 +377,13 @@ bool CPluginManager::addNewPluginRepo(const std::string& url, const std::string&
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
|
bool CPluginManager::removePluginRepo(const SPluginRepoIdentifier identifier) {
|
||||||
if (!DataState::pluginRepoExists(urlOrName)) {
|
if (!DataState::pluginRepoExists(identifier)) {
|
||||||
std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
|
std::println(stderr, "\n{}", failureString("Could not remove the repository. Repository is not installed."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << urlOrName << "\n "
|
std::cout << Colors::YELLOW << "!" << Colors::RESET << Colors::RED << " removing a plugin repository: " << Colors::RESET << identifier.toString() << "\n "
|
||||||
<< "Are you sure? [Y/n] ";
|
<< "Are you sure? [Y/n] ";
|
||||||
std::fflush(stdout);
|
std::fflush(stdout);
|
||||||
std::string input;
|
std::string input;
|
||||||
|
|
@ -383,7 +394,7 @@ bool CPluginManager::removePluginRepo(const std::string& urlOrName) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataState::removePluginRepo(urlOrName);
|
DataState::removePluginRepo(identifier);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -395,7 +406,7 @@ eHeadersErrors CPluginManager::headersValid() {
|
||||||
return HEADERS_MISSING;
|
return HEADERS_MISSING;
|
||||||
|
|
||||||
// find headers commit
|
// find headers commit
|
||||||
const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}/share/pkgconfig\" pkgconf --cflags --keep-system-cflags hyprland", DataState::getHeadersPath());
|
const std::string& cmd = std::format("PKG_CONFIG_PATH=\"{}\" pkgconf --cflags --keep-system-cflags hyprland", getPkgConfigPath());
|
||||||
auto headers = execAndGet(cmd);
|
auto headers = execAndGet(cmd);
|
||||||
|
|
||||||
if (!headers.contains("-I/"))
|
if (!headers.contains("-I/"))
|
||||||
|
|
@ -444,17 +455,22 @@ eHeadersErrors CPluginManager::headersValid() {
|
||||||
if (hash != HLVER.hash)
|
if (hash != HLVER.hash)
|
||||||
return HEADERS_MISMATCHED;
|
return HEADERS_MISMATCHED;
|
||||||
|
|
||||||
|
// check ABI hash too
|
||||||
|
const auto GLOBALSTATE = DataState::getGlobalState();
|
||||||
|
|
||||||
|
if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash)
|
||||||
|
return HEADERS_ABI_MISMATCH;
|
||||||
|
|
||||||
return HEADERS_OK;
|
return HEADERS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPluginManager::updateHeaders(bool force) {
|
bool CPluginManager::updateHeaders(bool force) {
|
||||||
|
|
||||||
DataState::ensureStateStoreExists();
|
DataState::ensureStateStoreExists();
|
||||||
|
|
||||||
const auto HLVER = getHyprlandVersion(false);
|
const auto HLVER = getHyprlandVersion(false);
|
||||||
|
|
||||||
if (!hasDeps()) {
|
if (!hasDeps()) {
|
||||||
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config, git, g++, gcc"));
|
std::println("\n{}", failureString("Could not update. Dependencies not satisfied. Hyprpm requires: cmake, cpio, pkg-config, git, g++, gcc"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -496,11 +512,11 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||||
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
|
progress.printMessageAbove(verboseString("will shallow since: {}", SHALLOW_DATE));
|
||||||
|
|
||||||
std::string ret =
|
std::string ret =
|
||||||
execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
|
execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}{}", getTempRoot(), HL_URL, USERNAME, (bShallow ? " --shallow-since='" + SHALLOW_DATE + "'" : "")));
|
||||||
|
|
||||||
if (!std::filesystem::exists(WORKINGDIR)) {
|
if (!std::filesystem::exists(WORKINGDIR)) {
|
||||||
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
|
progress.printMessageAbove(failureString("Clone failed. Retrying without shallow."));
|
||||||
ret = execAndGet(std::format("cd {} && git clone --recursive {} hyprland-{}", getTempRoot(), HL_URL, USERNAME));
|
ret = execAndGet(std::format("cd {} && git clone --recursive '{}' hyprland-{}", getTempRoot(), HL_URL, USERNAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
|
if (!std::filesystem::exists(WORKINGDIR + "/.git")) {
|
||||||
|
|
@ -543,8 +559,17 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||||
if (m_bVerbose)
|
if (m_bVerbose)
|
||||||
progress.printMessageAbove(verboseString("setting PREFIX for cmake to {}", DataState::getHeadersPath()));
|
progress.printMessageAbove(verboseString("setting PREFIX for cmake to {}", DataState::getHeadersPath()));
|
||||||
|
|
||||||
ret = execAndGet(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build -G Ninja", WORKINGDIR,
|
const auto CONFIGURE_CMD =
|
||||||
DataState::getHeadersPath()));
|
nixDevelopIfNeeded(std::format("cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING=\"{}\" -S . -B ./build", WORKINGDIR,
|
||||||
|
DataState::getHeadersPath()),
|
||||||
|
HLVER);
|
||||||
|
|
||||||
|
if (!CONFIGURE_CMD) {
|
||||||
|
std::println(stderr, "\n{}", failureString("Could not configure hyprland: {}", CONFIGURE_CMD.error()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = execAndGet(*CONFIGURE_CMD);
|
||||||
if (m_bVerbose)
|
if (m_bVerbose)
|
||||||
progress.printMessageAbove(verboseString("cmake returned: {}", ret));
|
progress.printMessageAbove(verboseString("cmake returned: {}", ret));
|
||||||
|
|
||||||
|
|
@ -589,14 +614,15 @@ bool CPluginManager::updateHeaders(bool force) {
|
||||||
std::filesystem::remove_all(WORKINGDIR);
|
std::filesystem::remove_all(WORKINGDIR);
|
||||||
|
|
||||||
auto HEADERSVALID = headersValid();
|
auto HEADERSVALID = headersValid();
|
||||||
if (HEADERSVALID == HEADERS_OK) {
|
|
||||||
|
if (HEADERSVALID == HEADERS_OK || HEADERSVALID == HEADERS_MISMATCHED || HEADERSVALID == HEADERS_ABI_MISMATCH) {
|
||||||
progress.printMessageAbove(successString("installed headers"));
|
progress.printMessageAbove(successString("installed headers"));
|
||||||
progress.m_iSteps = 5;
|
progress.m_iSteps = 5;
|
||||||
progress.m_szCurrentMessage = "Done!";
|
progress.m_szCurrentMessage = "Done!";
|
||||||
progress.print();
|
progress.print();
|
||||||
|
|
||||||
auto GLOBALSTATE = DataState::getGlobalState();
|
auto GLOBALSTATE = DataState::getGlobalState();
|
||||||
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
|
||||||
DataState::updateGlobalState(GLOBALSTATE);
|
DataState::updateGlobalState(GLOBALSTATE);
|
||||||
|
|
||||||
std::print("\n");
|
std::print("\n");
|
||||||
|
|
@ -631,7 +657,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
const auto HLVER = getHyprlandVersion(false);
|
const auto HLVER = getHyprlandVersion(false);
|
||||||
|
|
||||||
CProgressBar progress;
|
CProgressBar progress;
|
||||||
progress.m_iMaxSteps = REPOS.size() * 2 + 2;
|
progress.m_iMaxSteps = (REPOS.size() * 2) + 2;
|
||||||
progress.m_iSteps = 0;
|
progress.m_iSteps = 0;
|
||||||
progress.m_szCurrentMessage = "Updating repositories";
|
progress.m_szCurrentMessage = "Updating repositories";
|
||||||
progress.print();
|
progress.print();
|
||||||
|
|
@ -652,7 +678,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
|
|
||||||
progress.printMessageAbove(infoString("Cloning {}", repo.url));
|
progress.printMessageAbove(infoString("Cloning {}", repo.url));
|
||||||
|
|
||||||
std::string ret = execAndGet(std::format("cd {} && git clone --recursive {} {}", getTempRoot(), repo.url, USERNAME));
|
std::string ret = execAndGet(std::format("cd {} && git clone --recursive '{}' {}", getTempRoot(), repo.url, USERNAME));
|
||||||
|
|
||||||
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
if (!std::filesystem::exists(m_szWorkingPluginDirectory + "/.git")) {
|
||||||
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
|
std::println("{}", failureString("could not clone repo: shell returned: {}", ret));
|
||||||
|
|
@ -662,7 +688,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
if (!repo.rev.empty()) {
|
if (!repo.rev.empty()) {
|
||||||
progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
|
progress.printMessageAbove(infoString("Plugin has revision set, resetting: {}", repo.rev));
|
||||||
|
|
||||||
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo.rev);
|
std::string ret = execAndGet("git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules \'" + repo.rev + "\'");
|
||||||
if (ret.compare(0, 6, "fatal:") == 0) {
|
if (ret.compare(0, 6, "fatal:") == 0) {
|
||||||
std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
|
std::println(stderr, "\n{}", failureString("could not check out revision {}: shell returned:\n{}", repo.rev, ret));
|
||||||
|
|
||||||
|
|
@ -741,8 +767,14 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
progress.printMessageAbove(infoString("Building {}", p.name));
|
progress.printMessageAbove(infoString("Building {}", p.name));
|
||||||
|
|
||||||
for (auto const& bs : p.buildSteps) {
|
for (auto const& bs : p.buildSteps) {
|
||||||
const std::string& cmd = std::format("cd {} && PKG_CONFIG_PATH=\"{}/share/pkgconfig\" {}", m_szWorkingPluginDirectory, DataState::getHeadersPath(), bs);
|
const auto CMD_RAW = nixDevelopIfNeeded(std::format("cd {} && PKG_CONFIG_PATH=\"{}\" {}", m_szWorkingPluginDirectory, getPkgConfigPath(), bs), HLVER);
|
||||||
out += " -> " + cmd + "\n" + execAndGet(cmd) + "\n";
|
|
||||||
|
if (!CMD_RAW) {
|
||||||
|
progress.printMessageAbove(failureString("Failed to build {}: {}", p.name, CMD_RAW.error()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out += " -> " + *CMD_RAW + "\n" + execAndGet(*CMD_RAW) + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_bVerbose)
|
if (m_bVerbose)
|
||||||
|
|
@ -772,10 +804,10 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
repohash.pop_back();
|
repohash.pop_back();
|
||||||
newrepo.hash = repohash;
|
newrepo.hash = repohash;
|
||||||
for (auto const& p : pManifest->m_plugins) {
|
for (auto const& p : pManifest->m_plugins) {
|
||||||
const auto OLDPLUGINIT = std::find_if(repo.plugins.begin(), repo.plugins.end(), [&](const auto& other) { return other.name == p.name; });
|
const auto OLDPLUGINIT = std::ranges::find_if(repo.plugins, [&](const auto& other) { return other.name == p.name; });
|
||||||
newrepo.plugins.push_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
|
newrepo.plugins.emplace_back(SPlugin{p.name, m_szWorkingPluginDirectory + "/" + p.output, OLDPLUGINIT != repo.plugins.end() ? OLDPLUGINIT->enabled : false});
|
||||||
}
|
}
|
||||||
DataState::removePluginRepo(newrepo.name);
|
DataState::removePluginRepo(SPluginRepoIdentifier::fromName(newrepo.name));
|
||||||
DataState::addNewPluginRepo(newrepo);
|
DataState::addNewPluginRepo(newrepo);
|
||||||
|
|
||||||
std::filesystem::remove_all(m_szWorkingPluginDirectory);
|
std::filesystem::remove_all(m_szWorkingPluginDirectory);
|
||||||
|
|
@ -788,7 +820,7 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
progress.print();
|
progress.print();
|
||||||
|
|
||||||
auto GLOBALSTATE = DataState::getGlobalState();
|
auto GLOBALSTATE = DataState::getGlobalState();
|
||||||
GLOBALSTATE.headersHashCompiled = HLVER.hash;
|
GLOBALSTATE.headersAbiCompiled = HLVER.abiHash;
|
||||||
DataState::updateGlobalState(GLOBALSTATE);
|
DataState::updateGlobalState(GLOBALSTATE);
|
||||||
|
|
||||||
progress.m_iSteps++;
|
progress.m_iSteps++;
|
||||||
|
|
@ -800,17 +832,23 @@ bool CPluginManager::updatePlugins(bool forceUpdateAll) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPluginManager::enablePlugin(const std::string& name) {
|
bool CPluginManager::enablePlugin(const SPluginRepoIdentifier identifier) {
|
||||||
bool ret = DataState::setPluginEnabled(name, true);
|
bool ret = false;
|
||||||
|
|
||||||
|
switch (identifier.type) {
|
||||||
|
case IDENTIFIER_NAME:
|
||||||
|
case IDENTIFIER_AUTHOR_NAME: ret = DataState::setPluginEnabled(identifier, true); break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
if (ret)
|
if (ret)
|
||||||
std::println("{}", successString("Enabled {}", name));
|
std::println("{}", successString("Enabled {}", identifier.name));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPluginManager::disablePlugin(const std::string& name) {
|
bool CPluginManager::disablePlugin(const SPluginRepoIdentifier identifier) {
|
||||||
bool ret = DataState::setPluginEnabled(name, false);
|
bool ret = DataState::setPluginEnabled(identifier, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
std::println("{}", successString("Disabled {}", name));
|
std::println("{}", successString("Disabled {}", identifier.name));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -828,7 +866,7 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload)
|
||||||
}
|
}
|
||||||
const auto HYPRPMPATH = DataState::getDataStatePath();
|
const auto HYPRPMPATH = DataState::getDataStatePath();
|
||||||
|
|
||||||
const auto json = glz::read_json<glz::json_t::array_t>(NHyprlandSocket::send("j/plugins list"));
|
const auto json = glz::read_json<glz::generic::array_t>(NHyprlandSocket::send("j/plugins list"));
|
||||||
if (!json) {
|
if (!json) {
|
||||||
std::println(stderr, "PluginManager: couldn't parse plugin list output");
|
std::println(stderr, "PluginManager: couldn't parse plugin list output");
|
||||||
return LOADSTATE_FAIL;
|
return LOADSTATE_FAIL;
|
||||||
|
|
@ -893,7 +931,7 @@ ePluginLoadStateReturn CPluginManager::ensurePluginsLoadState(bool forceReload)
|
||||||
if (!p.enabled)
|
if (!p.enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!forceReload && std::find_if(loadedPlugins.begin(), loadedPlugins.end(), [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
|
if (!forceReload && std::ranges::find_if(loadedPlugins, [&](const auto& other) { return other == p.name; }) != loadedPlugins.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p.name) / p.filename, true)) {
|
if (!loadUnloadPlugin(HYPRPMPATH / repoForName(p.name) / p.filename, true)) {
|
||||||
|
|
@ -913,9 +951,9 @@ bool CPluginManager::loadUnloadPlugin(const std::string& path, bool load) {
|
||||||
auto state = DataState::getGlobalState();
|
auto state = DataState::getGlobalState();
|
||||||
auto HLVER = getHyprlandVersion(true);
|
auto HLVER = getHyprlandVersion(true);
|
||||||
|
|
||||||
if (state.headersHashCompiled != HLVER.hash) {
|
if (state.headersAbiCompiled != HLVER.abiHash) {
|
||||||
if (load)
|
if (load)
|
||||||
std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersHashCompiled));
|
std::println("{}", infoString("Running Hyprland version ({}) differs from plugin state ({}), please restart Hyprland.", HLVER.hash, state.headersAbiCompiled));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -931,7 +969,7 @@ void CPluginManager::listAllPlugins() {
|
||||||
const auto REPOS = DataState::getAllRepositories();
|
const auto REPOS = DataState::getAllRepositories();
|
||||||
|
|
||||||
for (auto const& r : REPOS) {
|
for (auto const& r : REPOS) {
|
||||||
std::println("{}", infoString("Repository {}:", r.name));
|
std::println("{}", infoString("Repository {} (by {}):", r.name, r.author));
|
||||||
|
|
||||||
for (auto const& p : r.plugins) {
|
for (auto const& p : r.plugins) {
|
||||||
std::println(" │ Plugin {}", p.name);
|
std::println(" │ Plugin {}", p.name);
|
||||||
|
|
@ -956,6 +994,7 @@ std::string CPluginManager::headerError(const eHeadersErrors err) {
|
||||||
case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n");
|
case HEADERS_MISMATCHED: return failureString("Headers version mismatch. Please run hyprpm update to fix those.\n");
|
||||||
case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n");
|
case HEADERS_NOT_HYPRLAND: return failureString("It doesn't seem you are running on hyprland.\n");
|
||||||
case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
|
case HEADERS_MISSING: return failureString("Headers missing. Please run hyprpm update to fix those.\n");
|
||||||
|
case HEADERS_ABI_MISMATCH: return failureString("ABI is mismatched. Please run hyprpm update to fix that.\n");
|
||||||
case HEADERS_DUPLICATED: {
|
case HEADERS_DUPLICATED: {
|
||||||
return failureString("Headers duplicated!!! This is a very bad sign.\n"
|
return failureString("Headers duplicated!!! This is a very bad sign.\n"
|
||||||
"This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n"
|
"This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed.\n"
|
||||||
|
|
@ -980,8 +1019,11 @@ std::string CPluginManager::headerErrorShort(const eHeadersErrors err) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPluginManager::hasDeps() {
|
bool CPluginManager::hasDeps() {
|
||||||
|
if (!m_bNoNix && getHyprlandVersion().isNix)
|
||||||
|
return true; // dep check not needed if we are on nix
|
||||||
|
|
||||||
bool hasAllDeps = true;
|
bool hasAllDeps = true;
|
||||||
std::vector<std::string> deps = {"meson", "cpio", "cmake", "pkg-config", "g++", "gcc", "git"};
|
std::vector<std::string> deps = {"cpio", "cmake", "pkg-config", "g++", "gcc", "git"};
|
||||||
|
|
||||||
for (auto const& d : deps) {
|
for (auto const& d : deps) {
|
||||||
if (!execAndGet("command -v " + d).contains("/")) {
|
if (!execAndGet("command -v " + d).contains("/")) {
|
||||||
|
|
@ -992,3 +1034,92 @@ bool CPluginManager::hasDeps() {
|
||||||
|
|
||||||
return hasAllDeps;
|
return hasAllDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& CPluginManager::getPkgConfigPath() {
|
||||||
|
static const auto str = std::format("{}/share/pkgconfig:$PKG_CONFIG_PATH", DataState::getHeadersPath());
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::expected<std::string, std::string> getNixDevelopFromPath(const std::string& argv0) {
|
||||||
|
std::string fullStorePath;
|
||||||
|
|
||||||
|
if (argv0.starts_with("/")) {
|
||||||
|
// we can use this directly
|
||||||
|
fullStorePath = argv0;
|
||||||
|
} else {
|
||||||
|
// use hyprpm, find in path
|
||||||
|
auto exe = NSys::findInPath("hyprpm");
|
||||||
|
if (!exe)
|
||||||
|
return std::unexpected("hyprpm not found in PATH");
|
||||||
|
|
||||||
|
fullStorePath = *exe;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullStorePath.empty() || !fullStorePath.ends_with("/bin/hyprpm"))
|
||||||
|
return std::unexpected("couldn't get a real path for hyprpm (1)");
|
||||||
|
|
||||||
|
// canonicalize to get the real nix-store path
|
||||||
|
std::error_code ec;
|
||||||
|
fullStorePath = std::filesystem::canonical(fullStorePath, ec);
|
||||||
|
|
||||||
|
if (ec || fullStorePath.empty() || !fullStorePath.starts_with("/nix"))
|
||||||
|
return std::unexpected("couldn't get a real path for hyprpm");
|
||||||
|
|
||||||
|
fullStorePath = fullStorePath.substr(0, fullStorePath.length() - std::string_view{"/bin/hyprpm"}.length());
|
||||||
|
|
||||||
|
auto deriver = trim(execAndGet(std::format("echo \"$(nix-store --query --deriver '{}')\"", fullStorePath)));
|
||||||
|
|
||||||
|
if (deriver.starts_with("unknown"))
|
||||||
|
return std::unexpected("couldn't nix deriver");
|
||||||
|
|
||||||
|
return deriver;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::expected<std::string, std::string> getNixDevelopFromProfile() {
|
||||||
|
const auto NIX_PROFILE_STR = execAndGet("nix profile list --json");
|
||||||
|
|
||||||
|
auto rawJson = glz::read_json<glz::generic>(NIX_PROFILE_STR);
|
||||||
|
|
||||||
|
if (!rawJson)
|
||||||
|
return std::unexpected("failed to parse nix profile list --json");
|
||||||
|
|
||||||
|
auto& json = *rawJson;
|
||||||
|
|
||||||
|
if (!json.contains("elements") || !json["elements"].is_object())
|
||||||
|
return std::unexpected("nix profile list --json returned a wonky json");
|
||||||
|
|
||||||
|
if (!json["elements"].contains("hyprland") && !json["elements"].contains("Hyprland"))
|
||||||
|
return std::unexpected("nix profile list --json doesn't contain Hyprland (did you uninstall?)");
|
||||||
|
|
||||||
|
auto& hyprlandJson = json["elements"].contains("hyprland") ? json["elements"]["hyprland"] : json["elements"]["Hyprland"];
|
||||||
|
|
||||||
|
if (!hyprlandJson.contains("originalUrl"))
|
||||||
|
return std::unexpected("nix profile list --json's hyprland doesn't contain originalUrl?");
|
||||||
|
|
||||||
|
return hyprlandJson["originalUrl"].get_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<std::string, std::string> CPluginManager::nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver) {
|
||||||
|
if (m_bNoNix || !ver.isNix)
|
||||||
|
return cmd;
|
||||||
|
|
||||||
|
// Escape single quotes
|
||||||
|
std::string newCmd = cmd;
|
||||||
|
replaceInString(newCmd, "'", "\\'");
|
||||||
|
|
||||||
|
auto NIX_DEVELOP = getNixDevelopFromPath(m_szArgv0);
|
||||||
|
|
||||||
|
if (NIX_DEVELOP)
|
||||||
|
return std::format("nix develop '{}' --command bash -c $'{}'", *NIX_DEVELOP, newCmd);
|
||||||
|
else if (m_bVerbose)
|
||||||
|
std::println("{}", verboseString("Failed nix from path: {}", NIX_DEVELOP.error()));
|
||||||
|
|
||||||
|
NIX_DEVELOP = getNixDevelopFromProfile();
|
||||||
|
|
||||||
|
if (NIX_DEVELOP)
|
||||||
|
return std::format("nix develop '{}' --command bash -c $'{}'", *NIX_DEVELOP, newCmd);
|
||||||
|
else if (m_bVerbose)
|
||||||
|
std::println("{}", verboseString("Failed nix from profile: {}", NIX_DEVELOP.error()));
|
||||||
|
|
||||||
|
return std::unexpected("hyprland is nix, but hyprpm failed to obtain a nix develop shell for build cmd");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <expected>
|
||||||
|
#include "Plugin.hpp"
|
||||||
|
|
||||||
enum eHeadersErrors {
|
enum eHeadersErrors {
|
||||||
HEADERS_OK = 0,
|
HEADERS_OK = 0,
|
||||||
|
|
@ -11,6 +13,7 @@ enum eHeadersErrors {
|
||||||
HEADERS_MISSING,
|
HEADERS_MISSING,
|
||||||
HEADERS_CORRUPTED,
|
HEADERS_CORRUPTED,
|
||||||
HEADERS_MISMATCHED,
|
HEADERS_MISMATCHED,
|
||||||
|
HEADERS_ABI_MISMATCH,
|
||||||
HEADERS_DUPLICATED
|
HEADERS_DUPLICATED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,7 +39,9 @@ struct SHyprlandVersion {
|
||||||
std::string branch;
|
std::string branch;
|
||||||
std::string hash;
|
std::string hash;
|
||||||
std::string date;
|
std::string date;
|
||||||
|
std::string abiHash;
|
||||||
int commits = 0;
|
int commits = 0;
|
||||||
|
bool isNix = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CPluginManager {
|
class CPluginManager {
|
||||||
|
|
@ -44,7 +49,7 @@ class CPluginManager {
|
||||||
CPluginManager();
|
CPluginManager();
|
||||||
|
|
||||||
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
bool addNewPluginRepo(const std::string& url, const std::string& rev);
|
||||||
bool removePluginRepo(const std::string& urlOrName);
|
bool removePluginRepo(const SPluginRepoIdentifier identifier);
|
||||||
|
|
||||||
eHeadersErrors headersValid();
|
eHeadersErrors headersValid();
|
||||||
bool updateHeaders(bool force = false);
|
bool updateHeaders(bool force = false);
|
||||||
|
|
@ -52,8 +57,8 @@ class CPluginManager {
|
||||||
|
|
||||||
void listAllPlugins();
|
void listAllPlugins();
|
||||||
|
|
||||||
bool enablePlugin(const std::string& name);
|
bool enablePlugin(const SPluginRepoIdentifier identifier);
|
||||||
bool disablePlugin(const std::string& name);
|
bool disablePlugin(const SPluginRepoIdentifier identifier);
|
||||||
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
|
ePluginLoadStateReturn ensurePluginsLoadState(bool forceReload = false);
|
||||||
|
|
||||||
bool loadUnloadPlugin(const std::string& path, bool load);
|
bool loadUnloadPlugin(const std::string& path, bool load);
|
||||||
|
|
@ -61,11 +66,14 @@ class CPluginManager {
|
||||||
|
|
||||||
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);
|
void notify(const eNotifyIcons icon, uint32_t color, int durationMs, const std::string& message);
|
||||||
|
|
||||||
|
const std::string& getPkgConfigPath();
|
||||||
|
|
||||||
bool hasDeps();
|
bool hasDeps();
|
||||||
|
|
||||||
bool m_bVerbose = false;
|
bool m_bVerbose = false;
|
||||||
bool m_bNoShallow = false;
|
bool m_bNoShallow = false;
|
||||||
std::string m_szCustomHlUrl, m_szUsername;
|
bool m_bNoNix = false;
|
||||||
|
std::string m_szCustomHlUrl, m_szUsername, m_szArgv0;
|
||||||
|
|
||||||
// will delete recursively if exists!!
|
// will delete recursively if exists!!
|
||||||
bool createSafeDirectory(const std::string& path);
|
bool createSafeDirectory(const std::string& path);
|
||||||
|
|
@ -73,6 +81,9 @@ class CPluginManager {
|
||||||
private:
|
private:
|
||||||
std::string headerError(const eHeadersErrors err);
|
std::string headerError(const eHeadersErrors err);
|
||||||
std::string headerErrorShort(const eHeadersErrors err);
|
std::string headerErrorShort(const eHeadersErrors err);
|
||||||
|
bool validArg(const std::string& s);
|
||||||
|
|
||||||
|
std::expected<std::string, std::string> nixDevelopIfNeeded(const std::string& cmd, const SHyprlandVersion& ver);
|
||||||
|
|
||||||
std::string m_szWorkingPluginDirectory;
|
std::string m_szWorkingPluginDirectory;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,13 @@ static std::string validSubinsAsStr() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool executableExistsInPath(const std::string& exe) {
|
static bool executableExistsInPath(const std::string& exe) {
|
||||||
|
return NSys::findInPath(exe).has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> NSys::findInPath(const std::string& exe) {
|
||||||
const char* PATHENV = std::getenv("PATH");
|
const char* PATHENV = std::getenv("PATH");
|
||||||
if (!PATHENV)
|
if (!PATHENV)
|
||||||
return false;
|
return std::nullopt;
|
||||||
|
|
||||||
CVarList paths(PATHENV, 0, ':', true);
|
CVarList paths(PATHENV, 0, ':', true);
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
|
@ -52,10 +56,10 @@ static bool executableExistsInPath(const std::string& exe) {
|
||||||
if (ec)
|
if (ec)
|
||||||
continue;
|
continue;
|
||||||
if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none)
|
if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none)
|
||||||
return true;
|
return candidate.string();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string subin() {
|
static std::string subin() {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace NSys {
|
namespace NSys {
|
||||||
bool isSuperuser();
|
bool isSuperuser();
|
||||||
int getUID();
|
int getUID();
|
||||||
int getEUID();
|
int getEUID();
|
||||||
|
std::optional<std::string> findInPath(const std::string& exe);
|
||||||
|
|
||||||
// NOLINTNEXTLINE
|
// NOLINTNEXTLINE
|
||||||
namespace root {
|
namespace root {
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ using namespace Hyprutils::Utils;
|
||||||
|
|
||||||
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
||||||
┃
|
┃
|
||||||
┣ add [url] [git rev] → Install a new plugin repository from git. Git revision
|
┣ add <url> [git rev] → Install a new plugin repository from git. Git revision
|
||||||
┃ is optional, when set, commit locks are ignored.
|
┃ is optional, when set, commit locks are ignored.
|
||||||
┣ remove [url/name] → Remove an installed plugin repository.
|
┣ remove <url|name|author/name> → Remove an installed plugin repository.
|
||||||
┣ enable [name] → Enable a plugin.
|
┣ enable <name|author/name> → Enable a plugin.
|
||||||
┣ disable [name] → Disable a plugin.
|
┣ disable <name|author/name> → Disable a plugin.
|
||||||
┣ update → Check and update all plugins if needed.
|
┣ update → Check and update all plugins if needed.
|
||||||
┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
|
┣ reload → Reload hyprpm state. Ensure all enabled plugins are loaded.
|
||||||
┣ list → List all installed plugins.
|
┣ list → List all installed plugins.
|
||||||
|
|
@ -25,8 +25,9 @@ constexpr std::string_view HELP = R"#(┏ hyprpm, a Hyprland Plugin Manager
|
||||||
┃
|
┃
|
||||||
┣ Flags:
|
┣ Flags:
|
||||||
┃
|
┃
|
||||||
┣ --notify | -n → Send a hyprland notification for important events (including both successes and fail events).
|
┣ --no-nix | → Disable `nix develop` for build commands, even if Hyprland is nix.
|
||||||
┣ --notify-fail | -nn → Send a hyprland notification for fail events only.
|
┣ --notify | -n → Send a hyprland notification confirming successful plugin load.
|
||||||
|
┃ Warnings/Errors trigger notifications regardless of this flag.
|
||||||
┣ --help | -h → Show this menu.
|
┣ --help | -h → Show this menu.
|
||||||
┣ --verbose | -v → Enable too much logging.
|
┣ --verbose | -v → Enable too much logging.
|
||||||
┣ --force | -f → Force an operation ignoring checks (e.g. update -f).
|
┣ --force | -f → Force an operation ignoring checks (e.g. update -f).
|
||||||
|
|
@ -47,7 +48,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> command;
|
std::vector<std::string> command;
|
||||||
bool notify = false, notifyFail = false, verbose = false, force = false, noShallow = false;
|
bool notify = false, verbose = false, force = false, noShallow = false, noNix = false;
|
||||||
std::string customHlUrl;
|
std::string customHlUrl;
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
|
@ -58,9 +59,13 @@ int main(int argc, char** argv, char** envp) {
|
||||||
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
|
} else if (ARGS[i] == "--notify" || ARGS[i] == "-n") {
|
||||||
notify = true;
|
notify = true;
|
||||||
} else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") {
|
} else if (ARGS[i] == "--notify-fail" || ARGS[i] == "-nn") {
|
||||||
notifyFail = notify = true;
|
// TODO: Deprecated since v.053.0. Remove in version>0.56.0
|
||||||
|
std::println(stderr, "{}", failureString("Deprececated flag."));
|
||||||
|
g_pPluginManager->notify(ICON_INFO, 0, 10000, "[hyprpm] -n flag is deprecated, see hyprpm --help.");
|
||||||
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
|
} else if (ARGS[i] == "--verbose" || ARGS[i] == "-v") {
|
||||||
verbose = true;
|
verbose = true;
|
||||||
|
} else if (ARGS[i] == "--no-nix") {
|
||||||
|
noNix = true;
|
||||||
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
|
} else if (ARGS[i] == "--no-shallow" || ARGS[i] == "-s") {
|
||||||
noShallow = true;
|
noShallow = true;
|
||||||
} else if (ARGS[i] == "--hl-url") {
|
} else if (ARGS[i] == "--hl-url") {
|
||||||
|
|
@ -89,7 +94,9 @@ int main(int argc, char** argv, char** envp) {
|
||||||
g_pPluginManager = std::make_unique<CPluginManager>();
|
g_pPluginManager = std::make_unique<CPluginManager>();
|
||||||
g_pPluginManager->m_bVerbose = verbose;
|
g_pPluginManager->m_bVerbose = verbose;
|
||||||
g_pPluginManager->m_bNoShallow = noShallow;
|
g_pPluginManager->m_bNoShallow = noShallow;
|
||||||
|
g_pPluginManager->m_bNoNix = noNix;
|
||||||
g_pPluginManager->m_szCustomHlUrl = customHlUrl;
|
g_pPluginManager->m_szCustomHlUrl = customHlUrl;
|
||||||
|
g_pPluginManager->m_szArgv0 = argv[0];
|
||||||
|
|
||||||
if (command[0] == "add") {
|
if (command[0] == "add") {
|
||||||
if (command.size() < 2) {
|
if (command.size() < 2) {
|
||||||
|
|
@ -104,7 +111,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
const auto HLVER = g_pPluginManager->getHyprlandVersion();
|
const auto HLVER = g_pPluginManager->getHyprlandVersion();
|
||||||
auto GLOBALSTATE = DataState::getGlobalState();
|
auto GLOBALSTATE = DataState::getGlobalState();
|
||||||
|
|
||||||
if (GLOBALSTATE.headersHashCompiled != HLVER.hash) {
|
if (GLOBALSTATE.headersAbiCompiled != HLVER.abiHash) {
|
||||||
std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update."));
|
std::println(stderr, "{}", failureString("Headers outdated, please run hyprpm update."));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +131,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
NSys::root::cacheSudo();
|
NSys::root::cacheSudo();
|
||||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||||
|
|
||||||
return g_pPluginManager->removePluginRepo(command[1]) ? 0 : 1;
|
return g_pPluginManager->removePluginRepo(SPluginRepoIdentifier::fromString(command[1])) ? 0 : 1;
|
||||||
} else if (command[0] == "update") {
|
} else if (command[0] == "update") {
|
||||||
NSys::root::cacheSudo();
|
NSys::root::cacheSudo();
|
||||||
CScopeGuard x([] { NSys::root::dropSudo(); });
|
CScopeGuard x([] { NSys::root::dropSudo(); });
|
||||||
|
|
@ -135,7 +142,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
if (headers) {
|
if (headers) {
|
||||||
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
|
const auto HLVER = g_pPluginManager->getHyprlandVersion(false);
|
||||||
auto GLOBALSTATE = DataState::getGlobalState();
|
auto GLOBALSTATE = DataState::getGlobalState();
|
||||||
const auto COMPILEDOUTDATED = HLVER.hash != GLOBALSTATE.headersHashCompiled;
|
const auto COMPILEDOUTDATED = HLVER.abiHash != GLOBALSTATE.headersAbiCompiled;
|
||||||
|
|
||||||
bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED);
|
bool ret1 = g_pPluginManager->updatePlugins(!headersValid || force || COMPILEDOUTDATED);
|
||||||
|
|
||||||
|
|
@ -149,15 +156,16 @@ int main(int argc, char** argv, char** envp) {
|
||||||
|
|
||||||
if (ret2 != LOADSTATE_OK)
|
if (ret2 != LOADSTATE_OK)
|
||||||
return 1;
|
return 1;
|
||||||
} else if (notify)
|
} else {
|
||||||
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
|
g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Couldn't update headers");
|
||||||
|
}
|
||||||
} else if (command[0] == "enable") {
|
} else if (command[0] == "enable") {
|
||||||
if (command.size() < 2) {
|
if (command.size() < 2) {
|
||||||
std::println(stderr, "{}", failureString("Not enough args for enable."));
|
std::println(stderr, "{}", failureString("Not enough args for enable."));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_pPluginManager->enablePlugin(command[1])) {
|
if (!g_pPluginManager->enablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
|
||||||
std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
|
std::println(stderr, "{}", failureString("Couldn't enable plugin (missing?)"));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +186,7 @@ int main(int argc, char** argv, char** envp) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_pPluginManager->disablePlugin(command[1])) {
|
if (!g_pPluginManager->disablePlugin(SPluginRepoIdentifier::fromString(command[1]))) {
|
||||||
std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
|
std::println(stderr, "{}", failureString("Couldn't disable plugin (missing?)"));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +202,6 @@ int main(int argc, char** argv, char** envp) {
|
||||||
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
|
auto ret = g_pPluginManager->ensurePluginsLoadState(force);
|
||||||
|
|
||||||
if (ret != LOADSTATE_OK) {
|
if (ret != LOADSTATE_OK) {
|
||||||
if (notify) {
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case LOADSTATE_FAIL:
|
case LOADSTATE_FAIL:
|
||||||
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
|
case LOADSTATE_PARTIAL_FAIL: g_pPluginManager->notify(ICON_ERROR, 0, 10000, "[hyprpm] Failed to load plugins"); break;
|
||||||
|
|
@ -203,10 +210,9 @@ int main(int argc, char** argv, char** envp) {
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
} else if (notify && !notifyFail) {
|
} else if (notify) {
|
||||||
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
|
g_pPluginManager->notify(ICON_OK, 0, 4000, "[hyprpm] Loaded plugins");
|
||||||
}
|
}
|
||||||
} else if (command[0] == "purge-cache") {
|
} else if (command[0] == "purge-cache") {
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
globber = run_command('sh', '-c', 'find . -name "*.cpp" | sort', check: true)
|
|
||||||
src = globber.stdout().strip().split('\n')
|
|
||||||
|
|
||||||
executable(
|
|
||||||
'hyprpm',
|
|
||||||
src,
|
|
||||||
dependencies: [
|
|
||||||
dependency('hyprutils', version: '>= 0.1.1'),
|
|
||||||
dependency('threads'),
|
|
||||||
dependency('tomlplusplus'),
|
|
||||||
dependency('glaze', method: 'cmake'),
|
|
||||||
],
|
|
||||||
install: true,
|
|
||||||
)
|
|
||||||
|
|
||||||
install_data(
|
|
||||||
'../hyprpm.bash',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'bash-completion/completions'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
rename: 'hyprpm',
|
|
||||||
)
|
|
||||||
install_data(
|
|
||||||
'../hyprpm.fish',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'fish/vendor_completions.d'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
)
|
|
||||||
install_data(
|
|
||||||
'../hyprpm.zsh',
|
|
||||||
install_dir: join_paths(get_option('datadir'), 'zsh/site-functions'),
|
|
||||||
install_tag: 'runtime',
|
|
||||||
rename: '_hyprpm',
|
|
||||||
)
|
|
||||||
|
|
@ -96,6 +96,9 @@ endfunction()
|
||||||
|
|
||||||
protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
|
protocolnew("staging/pointer-warp" "pointer-warp-v1" false)
|
||||||
protocolnew("stable/xdg-shell" "xdg-shell" false)
|
protocolnew("stable/xdg-shell" "xdg-shell" false)
|
||||||
|
protocolnew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false)
|
||||||
|
|
||||||
clientNew("pointer-warp" PROTOS "pointer-warp-v1" "xdg-shell")
|
clientNew("pointer-warp" PROTOS "pointer-warp-v1" "xdg-shell")
|
||||||
clientNew("pointer-scroll" PROTOS "xdg-shell")
|
clientNew("pointer-scroll" PROTOS "xdg-shell")
|
||||||
|
clientNew("child-window" PROTOS "xdg-shell")
|
||||||
|
clientNew("shortcut-inhibitor" PROTOS "xdg-shell" "keyboard-shortcuts-inhibit-unstable-v1")
|
||||||
|
|
|
||||||
336
hyprtester/clients/child-window.cpp
Normal file
336
hyprtester/clients/child-window.cpp
Normal file
|
|
@ -0,0 +1,336 @@
|
||||||
|
#include <print>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <wayland.hpp>
|
||||||
|
#include <xdg-shell.hpp>
|
||||||
|
|
||||||
|
#include <hyprutils/memory/SharedPtr.hpp>
|
||||||
|
#include <hyprutils/math/Vector2D.hpp>
|
||||||
|
|
||||||
|
using Hyprutils::Math::Vector2D;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
struct SWlState {
|
||||||
|
wl_display* display;
|
||||||
|
CSharedPointer<CCWlRegistry> registry;
|
||||||
|
|
||||||
|
// protocols
|
||||||
|
CSharedPointer<CCWlCompositor> wlCompositor;
|
||||||
|
CSharedPointer<CCWlSeat> wlSeat;
|
||||||
|
CSharedPointer<CCWlShm> wlShm;
|
||||||
|
CSharedPointer<CCXdgWmBase> xdgShell;
|
||||||
|
|
||||||
|
// shm/buffer stuff
|
||||||
|
CSharedPointer<CCWlShmPool> shmPool;
|
||||||
|
CSharedPointer<CCWlBuffer> shmBuf;
|
||||||
|
CSharedPointer<CCWlBuffer> shmBuf2;
|
||||||
|
int shmFd = 0;
|
||||||
|
size_t shmBufSize = 0;
|
||||||
|
bool xrgb8888_support = false;
|
||||||
|
|
||||||
|
// surface/toplevel stuff
|
||||||
|
CSharedPointer<CCWlSurface> surf;
|
||||||
|
CSharedPointer<CCXdgSurface> xdgSurf;
|
||||||
|
CSharedPointer<CCXdgToplevel> xdgToplevel;
|
||||||
|
Vector2D geom;
|
||||||
|
|
||||||
|
// pointer
|
||||||
|
CSharedPointer<CCWlPointer> pointer;
|
||||||
|
uint32_t enterSerial = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool debug, shouldExit, started;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
//NOLINTNEXTLINE
|
||||||
|
static void clientLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||||
|
std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||||
|
std::println("{}", text);
|
||||||
|
std::fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
//NOLINTNEXTLINE
|
||||||
|
static void debugLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||||
|
std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||||
|
if (!debug)
|
||||||
|
return;
|
||||||
|
std::println("{}", text);
|
||||||
|
std::fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bindRegistry(SWlState& state) {
|
||||||
|
state.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(state.display));
|
||||||
|
|
||||||
|
state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) {
|
||||||
|
const std::string NAME = name;
|
||||||
|
if (NAME == "wl_compositor") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.wlCompositor = makeShared<CCWlCompositor>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6));
|
||||||
|
} else if (NAME == "wl_shm") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.wlShm = makeShared<CCWlShm>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1));
|
||||||
|
} else if (NAME == "wl_seat") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.wlSeat = makeShared<CCWlSeat>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9));
|
||||||
|
} else if (NAME == "xdg_wm_base") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.xdgShell = makeShared<CCXdgWmBase>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
state.registry->setGlobalRemove([](CCWlRegistry* r, uint32_t id) { debugLog("Global {} removed", id); });
|
||||||
|
|
||||||
|
wl_display_roundtrip(state.display);
|
||||||
|
|
||||||
|
if (!state.wlCompositor || !state.wlShm || !state.wlSeat || !state.xdgShell) {
|
||||||
|
clientLog("Failed to get protocols from Hyprland");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool createShm(SWlState& state, Vector2D geom) {
|
||||||
|
if (!state.xrgb8888_support)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t stride = geom.x * 4;
|
||||||
|
size_t size = geom.y * stride;
|
||||||
|
if (!state.shmPool) {
|
||||||
|
const char* name = "/wl-shm-pointer-warp";
|
||||||
|
state.shmFd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||||
|
if (state.shmFd < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (shm_unlink(name) < 0 || ftruncate(state.shmFd, size * 2) < 0) {
|
||||||
|
close(state.shmFd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.shmPool = makeShared<CCWlShmPool>(state.wlShm->sendCreatePool(state.shmFd, size * 2));
|
||||||
|
if (!state.shmPool->resource()) {
|
||||||
|
close(state.shmFd);
|
||||||
|
state.shmFd = -1;
|
||||||
|
state.shmPool.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
state.shmBufSize = size;
|
||||||
|
} else if (size > state.shmBufSize) {
|
||||||
|
if (ftruncate(state.shmFd, size) < 0) {
|
||||||
|
close(state.shmFd);
|
||||||
|
state.shmFd = -1;
|
||||||
|
state.shmPool.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.shmPool->sendResize(size * 2);
|
||||||
|
state.shmBufSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buf = makeShared<CCWlBuffer>(state.shmPool->sendCreateBuffer(0, geom.x, geom.y, stride, WL_SHM_FORMAT_XRGB8888));
|
||||||
|
if (!buf->resource())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (state.shmBuf) {
|
||||||
|
state.shmBuf->sendDestroy();
|
||||||
|
state.shmBuf.reset();
|
||||||
|
}
|
||||||
|
state.shmBuf = buf;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setupToplevel(SWlState& state) {
|
||||||
|
state.wlShm->setFormat([&](CCWlShm* p, uint32_t format) {
|
||||||
|
if (format == WL_SHM_FORMAT_XRGB8888)
|
||||||
|
state.xrgb8888_support = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
state.xdgShell->setPing([&](CCXdgWmBase* p, uint32_t serial) { state.xdgShell->sendPong(serial); });
|
||||||
|
|
||||||
|
state.surf = makeShared<CCWlSurface>(state.wlCompositor->sendCreateSurface());
|
||||||
|
if (!state.surf->resource())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.xdgSurf = makeShared<CCXdgSurface>(state.xdgShell->sendGetXdgSurface(state.surf->resource()));
|
||||||
|
if (!state.xdgSurf->resource())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.xdgToplevel = makeShared<CCXdgToplevel>(state.xdgSurf->sendGetToplevel());
|
||||||
|
if (!state.xdgToplevel->resource())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.xdgToplevel->setClose([&](CCXdgToplevel* p) { exit(0); });
|
||||||
|
|
||||||
|
state.xdgToplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) {
|
||||||
|
state.geom = {1280, 720};
|
||||||
|
|
||||||
|
if (!createShm(state, state.geom))
|
||||||
|
exit(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
state.xdgSurf->setConfigure([&](CCXdgSurface* p, uint32_t serial) {
|
||||||
|
if (!state.shmBuf)
|
||||||
|
debugLog("xdgSurf configure but no buf made yet?");
|
||||||
|
|
||||||
|
state.xdgSurf->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y);
|
||||||
|
state.surf->sendAttach(state.shmBuf.get(), 0, 0);
|
||||||
|
state.surf->sendCommit();
|
||||||
|
|
||||||
|
state.xdgSurf->sendAckConfigure(serial);
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
started = true;
|
||||||
|
clientLog("started");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
state.xdgToplevel->sendSetTitle("child-test parent");
|
||||||
|
state.xdgToplevel->sendSetAppId("child-test-parent");
|
||||||
|
|
||||||
|
state.surf->sendAttach(nullptr, 0, 0);
|
||||||
|
state.surf->sendCommit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setupSeat(SWlState& state) {
|
||||||
|
state.pointer = makeShared<CCWlPointer>(state.wlSeat->sendGetPointer());
|
||||||
|
if (!state.pointer->resource())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.pointer->setEnter([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf, wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
debugLog("Got pointer enter event, serial {}, x {}, y {}", serial, x, y);
|
||||||
|
state.enterSerial = serial;
|
||||||
|
});
|
||||||
|
|
||||||
|
state.pointer->setLeave([&](CCWlPointer* p, uint32_t serial, wl_proxy* surf) { debugLog("Got pointer leave event, serial {}", serial); });
|
||||||
|
|
||||||
|
state.pointer->setMotion([&](CCWlPointer* p, uint32_t serial, wl_fixed_t x, wl_fixed_t y) { debugLog("Got pointer motion event, serial {}, x {}, y {}", serial, x, y); });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SChildWindow {
|
||||||
|
CSharedPointer<CCWlSurface> surface;
|
||||||
|
CSharedPointer<CCXdgSurface> xSurface;
|
||||||
|
CSharedPointer<CCXdgToplevel> toplevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void parseRequest(SWlState& state, std::string str, SChildWindow& window) {
|
||||||
|
if (str.starts_with("exit")) {
|
||||||
|
shouldExit = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = str.find_first_of('\n');
|
||||||
|
str = str.substr(0, index);
|
||||||
|
|
||||||
|
if (str == "toplevel") {
|
||||||
|
window.surface = makeShared<CCWlSurface>(state.wlCompositor->sendCreateSurface());
|
||||||
|
window.xSurface = makeShared<CCXdgSurface>(state.xdgShell->sendGetXdgSurface(window.surface->resource()));
|
||||||
|
|
||||||
|
window.xSurface->setConfigure([&](CCXdgSurface* p, uint32_t serial) {
|
||||||
|
if (!state.shmBuf)
|
||||||
|
debugLog("xdgSurf configure but no buf made yet?");
|
||||||
|
|
||||||
|
window.xSurface->sendSetWindowGeometry(0, 0, state.geom.x, state.geom.y);
|
||||||
|
window.surface->sendAttach(state.shmBuf2.get(), 0, 0);
|
||||||
|
window.surface->sendCommit();
|
||||||
|
|
||||||
|
window.xSurface->sendAckConfigure(serial);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.toplevel = makeShared<CCXdgToplevel>(window.xSurface->sendGetToplevel());
|
||||||
|
|
||||||
|
window.toplevel->setConfigure([&](CCXdgToplevel* p, int32_t w, int32_t h, wl_array* arr) {
|
||||||
|
size_t stride = 1280 * 4;
|
||||||
|
size_t size = 720 * stride;
|
||||||
|
|
||||||
|
auto buf = makeShared<CCWlBuffer>(state.shmPool->sendCreateBuffer(size, state.geom.x, state.geom.y, stride, WL_SHM_FORMAT_XRGB8888));
|
||||||
|
if (!buf->resource())
|
||||||
|
clientLog("Failed to create child buffer");
|
||||||
|
|
||||||
|
if (state.shmBuf2) {
|
||||||
|
state.shmBuf2->sendDestroy();
|
||||||
|
state.shmBuf2.reset();
|
||||||
|
}
|
||||||
|
state.shmBuf2 = buf;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.toplevel->sendSetTitle("child-test child");
|
||||||
|
window.toplevel->sendSetAppId("child-test-child");
|
||||||
|
window.toplevel->sendSetParent(state.xdgToplevel.get());
|
||||||
|
|
||||||
|
window.surface->sendAttach(nullptr, 0, 0);
|
||||||
|
window.surface->sendCommit();
|
||||||
|
clientLog("child started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc != 1 && argc != 2)
|
||||||
|
clientLog("Only the \"--debug\" switch is allowed, it turns on debug logs.");
|
||||||
|
|
||||||
|
if (argc == 2 && std::string{argv[1]} == "--debug")
|
||||||
|
debug = true;
|
||||||
|
|
||||||
|
SWlState state;
|
||||||
|
SChildWindow window;
|
||||||
|
|
||||||
|
// WAYLAND_DISPLAY env should be set to the correct one
|
||||||
|
state.display = wl_display_connect(nullptr);
|
||||||
|
if (!state.display) {
|
||||||
|
clientLog("Failed to connect to wayland display");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bindRegistry(state) || !setupSeat(state) || !setupToplevel(state))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
std::array<char, 1024> readBuf;
|
||||||
|
readBuf.fill(0);
|
||||||
|
|
||||||
|
wl_display_flush(state.display);
|
||||||
|
|
||||||
|
struct pollfd fds[2] = {{.fd = wl_display_get_fd(state.display), .events = POLLIN | POLLOUT}, {.fd = STDIN_FILENO, .events = POLLIN}};
|
||||||
|
while (!shouldExit && poll(fds, 2, 0) != -1) {
|
||||||
|
if (fds[0].revents & POLLIN) {
|
||||||
|
wl_display_flush(state.display);
|
||||||
|
|
||||||
|
if (wl_display_prepare_read(state.display) == 0) {
|
||||||
|
wl_display_read_events(state.display);
|
||||||
|
wl_display_dispatch_pending(state.display);
|
||||||
|
} else
|
||||||
|
wl_display_dispatch(state.display);
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
do {
|
||||||
|
ret = wl_display_dispatch_pending(state.display);
|
||||||
|
wl_display_flush(state.display);
|
||||||
|
} while (ret > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fds[1].revents & POLLIN) {
|
||||||
|
ssize_t bytesRead = read(fds[1].fd, readBuf.data(), 1023);
|
||||||
|
if (bytesRead == -1)
|
||||||
|
continue;
|
||||||
|
readBuf[bytesRead] = 0;
|
||||||
|
|
||||||
|
parseRequest(state, std::string{readBuf.data()}, window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_display* display = state.display;
|
||||||
|
state = {};
|
||||||
|
window = {};
|
||||||
|
|
||||||
|
wl_display_disconnect(display);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
||||||
297
hyprtester/clients/shortcut-inhibitor.cpp
Normal file
297
hyprtester/clients/shortcut-inhibitor.cpp
Normal file
|
|
@ -0,0 +1,297 @@
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <print>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <wayland.hpp>
|
||||||
|
#include <xdg-shell.hpp>
|
||||||
|
#include <keyboard-shortcuts-inhibit-unstable-v1.hpp>
|
||||||
|
|
||||||
|
#include <hyprutils/memory/SharedPtr.hpp>
|
||||||
|
#include <hyprutils/math/Vector2D.hpp>
|
||||||
|
|
||||||
|
using Hyprutils::Math::Vector2D;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
struct SWlState {
|
||||||
|
wl_display* display;
|
||||||
|
CSharedPointer<CCWlRegistry> registry;
|
||||||
|
|
||||||
|
// protocols
|
||||||
|
CSharedPointer<CCWlCompositor> wlCompositor;
|
||||||
|
CSharedPointer<CCWlSeat> wlSeat;
|
||||||
|
CSharedPointer<CCWlShm> wlShm;
|
||||||
|
CSharedPointer<CCXdgWmBase> xdgShell;
|
||||||
|
CSharedPointer<CCZwpKeyboardShortcutsInhibitManagerV1> inhibitManager;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// shortcut inhibiting
|
||||||
|
CSharedPointer<CCZwpKeyboardShortcutsInhibitorV1> inhibitor;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool debug, started, shouldExit;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
//NOLINTNEXTLINE
|
||||||
|
static void clientLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||||
|
std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||||
|
std::println("{}", text);
|
||||||
|
std::fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
//NOLINTNEXTLINE
|
||||||
|
static void debugLog(std::format_string<Args...> fmt, Args&&... args) {
|
||||||
|
std::string text = std::vformat(fmt.get(), std::make_format_args(args...));
|
||||||
|
if (!debug)
|
||||||
|
return;
|
||||||
|
std::println("{}", text);
|
||||||
|
std::fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool bindRegistry(SWlState& state) {
|
||||||
|
state.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(state.display));
|
||||||
|
|
||||||
|
state.registry->setGlobal([&](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) {
|
||||||
|
const std::string NAME = name;
|
||||||
|
if (NAME == "wl_compositor") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.wlCompositor = makeShared<CCWlCompositor>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_compositor_interface, 6));
|
||||||
|
} else if (NAME == "wl_shm") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.wlShm = makeShared<CCWlShm>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_shm_interface, 1));
|
||||||
|
} else if (NAME == "wl_seat") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.wlSeat = makeShared<CCWlSeat>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &wl_seat_interface, 9));
|
||||||
|
} else if (NAME == "xdg_wm_base") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.xdgShell = makeShared<CCXdgWmBase>((wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &xdg_wm_base_interface, 1));
|
||||||
|
} else if (NAME == "zwp_keyboard_shortcuts_inhibit_manager_v1") {
|
||||||
|
debugLog(" > binding to global: {} (version {}) with id {}", name, version, id);
|
||||||
|
state.inhibitManager = makeShared<CCZwpKeyboardShortcutsInhibitManagerV1>(
|
||||||
|
(wl_proxy*)wl_registry_bind((wl_registry*)state.registry->resource(), id, &zwp_keyboard_shortcuts_inhibit_manager_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.inhibitManager) {
|
||||||
|
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-shortcut-inhibitor";
|
||||||
|
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("shortcut-inhibitor test client");
|
||||||
|
state.xdgToplevel->sendSetAppId("shortcut-inhibitor");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parseRequest(SWlState& state, std::string req) {
|
||||||
|
if (req.starts_with("on")) {
|
||||||
|
state.inhibitor = makeShared<CCZwpKeyboardShortcutsInhibitorV1>(state.inhibitManager->sendInhibitShortcuts(state.surf->resource(), state.wlSeat->resource()));
|
||||||
|
|
||||||
|
state.inhibitor->setActive([&](CCZwpKeyboardShortcutsInhibitorV1* inhibitorV1) { clientLog("inhibiting"); });
|
||||||
|
state.inhibitor->setInactive([&](CCZwpKeyboardShortcutsInhibitorV1* inhibitorV1) { clientLog("inhibit disabled by compositor"); });
|
||||||
|
} else if (req.starts_with("off")) {
|
||||||
|
state.inhibitor->sendDestroy();
|
||||||
|
state.inhibitor.reset();
|
||||||
|
shouldExit = true;
|
||||||
|
clientLog("inhibit disabled by request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -6,12 +6,16 @@
|
||||||
#define private public
|
#define private public
|
||||||
#include <src/config/ConfigManager.hpp>
|
#include <src/config/ConfigManager.hpp>
|
||||||
#include <src/config/ConfigDescriptions.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/InputManager.hpp>
|
||||||
#include <src/managers/PointerManager.hpp>
|
#include <src/managers/PointerManager.hpp>
|
||||||
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
||||||
|
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
|
||||||
|
#include <src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp>
|
||||||
|
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
||||||
|
#include <src/desktop/view/LayerSurface.hpp>
|
||||||
#include <src/Compositor.hpp>
|
#include <src/Compositor.hpp>
|
||||||
|
#include <src/desktop/state/FocusState.hpp>
|
||||||
|
#include <src/layout/LayoutManager.hpp>
|
||||||
#undef private
|
#undef private
|
||||||
|
|
||||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
|
|
@ -43,15 +47,16 @@ static SDispatchResult test(std::string in) {
|
||||||
|
|
||||||
// Trigger a snap move event for the active window
|
// Trigger a snap move event for the active window
|
||||||
static SDispatchResult snapMove(std::string in) {
|
static SDispatchResult snapMove(std::string in) {
|
||||||
const auto PLASTWINDOW = g_pCompositor->m_lastWindow.lock();
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||||
if (!PLASTWINDOW->m_isFloating)
|
if (!PLASTWINDOW->m_isFloating)
|
||||||
return {.success = false, .error = "Window must be floating"};
|
return {.success = false, .error = "Window must be floating"};
|
||||||
|
|
||||||
Vector2D pos = PLASTWINDOW->m_realPosition->goal();
|
Vector2D pos = PLASTWINDOW->m_realPosition->goal();
|
||||||
Vector2D size = PLASTWINDOW->m_realSize->goal();
|
Vector2D size = PLASTWINDOW->m_realSize->goal();
|
||||||
|
|
||||||
g_pLayoutManager->getCurrentLayout()->performSnap(pos, size, PLASTWINDOW, MBIND_MOVE, -1, size);
|
g_layoutManager->performSnap(pos, size, PLASTWINDOW->layoutTarget(), MBIND_MOVE, -1, size);
|
||||||
*PLASTWINDOW->m_realPosition = pos.round();
|
|
||||||
|
PLASTWINDOW->layoutTarget()->setPositionGlobal(CBox{pos, size});
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
@ -210,7 +215,7 @@ static SDispatchResult scroll(std::string in) {
|
||||||
by = std::stod(in);
|
by = std::stod(in);
|
||||||
} catch (...) { return SDispatchResult{.success = false, .error = "invalid input"}; }
|
} catch (...) { return SDispatchResult{.success = false, .error = "invalid input"}; }
|
||||||
|
|
||||||
Debug::log(LOG, "tester: scrolling by {}", by);
|
Log::logger->log(Log::DEBUG, "tester: scrolling by {}", by);
|
||||||
|
|
||||||
g_mouse->m_pointerEvents.axis.emit(IPointer::SAxisEvent{
|
g_mouse->m_pointerEvents.axis.emit(IPointer::SAxisEvent{
|
||||||
.delta = by,
|
.delta = by,
|
||||||
|
|
@ -221,8 +226,30 @@ static SDispatchResult scroll(std::string in) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SDispatchResult click(std::string in) {
|
||||||
|
CVarList2 data(std::move(in));
|
||||||
|
|
||||||
|
uint32_t button;
|
||||||
|
bool pressed;
|
||||||
|
try {
|
||||||
|
button = std::stoul(std::string{data[0]});
|
||||||
|
pressed = std::stoul(std::string{data[1]}) == 1;
|
||||||
|
} catch (...) { return {.success = false, .error = "invalid input"}; }
|
||||||
|
|
||||||
|
Log::logger->log(Log::DEBUG, "tester: mouse button {} state {}", button, pressed);
|
||||||
|
|
||||||
|
g_mouse->m_pointerEvents.button.emit(IPointer::SButtonEvent{
|
||||||
|
.timeMs = sc<uint32_t>(Time::millis(Time::steadyNow())),
|
||||||
|
.button = button,
|
||||||
|
.state = pressed ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED,
|
||||||
|
.mouse = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
static SDispatchResult keybind(std::string in) {
|
static SDispatchResult keybind(std::string in) {
|
||||||
CVarList data(in);
|
CVarList2 data(std::move(in));
|
||||||
// 0 = release, 1 = press
|
// 0 = release, 1 = press
|
||||||
bool press;
|
bool press;
|
||||||
// See src/devices/IKeyboard.hpp : eKeyboardModifiers for modifier bitmasks
|
// See src/devices/IKeyboard.hpp : eKeyboardModifiers for modifier bitmasks
|
||||||
|
|
@ -231,9 +258,9 @@ static SDispatchResult keybind(std::string in) {
|
||||||
// keycode
|
// keycode
|
||||||
uint32_t key;
|
uint32_t key;
|
||||||
try {
|
try {
|
||||||
press = std::stoul(data[0]) == 1;
|
press = std::stoul(std::string{data[0]}) == 1;
|
||||||
modifier = std::stoul(data[1]);
|
modifier = std::stoul(std::string{data[1]});
|
||||||
key = std::stoul(data[2]) - 8; // xkb offset
|
key = std::stoul(std::string{data[2]}) - 8; // xkb offset
|
||||||
} catch (...) { return {.success = false, .error = "invalid input"}; }
|
} catch (...) { return {.success = false, .error = "invalid input"}; }
|
||||||
|
|
||||||
uint32_t modifierMask = 0;
|
uint32_t modifierMask = 0;
|
||||||
|
|
@ -245,6 +272,85 @@ static SDispatchResult keybind(std::string in) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Desktop::Rule::CWindowRuleEffectContainer::storageType windowRuleIDX = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
static SDispatchResult addWindowRule(std::string in) {
|
||||||
|
windowRuleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
|
||||||
|
|
||||||
|
if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != windowRuleIDX)
|
||||||
|
return {.success = false, .error = "re-registering returned a different id?"};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDispatchResult checkWindowRule(std::string in) {
|
||||||
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||||
|
|
||||||
|
if (!PLASTWINDOW)
|
||||||
|
return {.success = false, .error = "No window"};
|
||||||
|
|
||||||
|
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(windowRuleIDX))
|
||||||
|
return {.success = false, .error = "No rule"};
|
||||||
|
|
||||||
|
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[windowRuleIDX]->effect != "effect")
|
||||||
|
return {.success = false, .error = "Effect isn't \"effect\""};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Desktop::Rule::CLayerRuleEffectContainer::storageType layerRuleIDX = 0;
|
||||||
|
|
||||||
|
static SDispatchResult addLayerRule(std::string in) {
|
||||||
|
layerRuleIDX = Desktop::Rule::layerEffects()->registerEffect("plugin_rule");
|
||||||
|
|
||||||
|
if (Desktop::Rule::layerEffects()->registerEffect("plugin_rule") != layerRuleIDX)
|
||||||
|
return {.success = false, .error = "re-registering returned a different id?"};
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDispatchResult checkLayerRule(std::string in) {
|
||||||
|
if (g_pCompositor->m_layers.size() != 3)
|
||||||
|
return {.success = false, .error = "Layers under test not here"};
|
||||||
|
|
||||||
|
for (const auto& layer : g_pCompositor->m_layers) {
|
||||||
|
if (layer->m_namespace == "rule-layer") {
|
||||||
|
|
||||||
|
if (!layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX))
|
||||||
|
return {.success = false, .error = "No rule"};
|
||||||
|
|
||||||
|
if (layer->m_ruleApplicator->m_otherProps.props[layerRuleIDX]->effect != "effect")
|
||||||
|
return {.success = false, .error = "Effect isn't \"effect\""};
|
||||||
|
|
||||||
|
} else if (layer->m_namespace == "norule-layer") {
|
||||||
|
|
||||||
|
if (layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX))
|
||||||
|
return {.success = false, .error = "Rule even though it shouldn't"};
|
||||||
|
|
||||||
|
} else
|
||||||
|
return {.success = false, .error = "Unrecognized layer"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDispatchResult floatingFocusOnFullscreen(std::string in) {
|
||||||
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||||
|
|
||||||
|
if (!PLASTWINDOW)
|
||||||
|
return {.success = false, .error = "No window"};
|
||||||
|
|
||||||
|
if (!PLASTWINDOW->m_isFloating)
|
||||||
|
return {.success = false, .error = "Window must be floating"};
|
||||||
|
|
||||||
|
if (PLASTWINDOW->m_alpha != 1.f)
|
||||||
|
return {.success = false, .error = "floating window doesnt restore it opacity when focused on fullscreen workspace"};
|
||||||
|
|
||||||
|
if (!PLASTWINDOW->m_createdOverFullscreen)
|
||||||
|
return {.success = false, .error = "floating window doesnt get flagged as createdOverFullscreen"};
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
PHANDLE = handle;
|
PHANDLE = handle;
|
||||||
|
|
||||||
|
|
@ -254,7 +360,13 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
|
||||||
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:click", ::click);
|
||||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
|
||||||
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_window_rule", ::addWindowRule);
|
||||||
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_window_rule", ::checkWindowRule);
|
||||||
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_layer_rule", ::addLayerRule);
|
||||||
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_layer_rule", ::checkLayerRule);
|
||||||
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:floating_focus_on_fullscreen", ::floatingFocusOnFullscreen);
|
||||||
|
|
||||||
// init mouse
|
// init mouse
|
||||||
g_mouse = CTestMouse::create(false);
|
g_mouse = CTestMouse::create(false);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,17 @@ namespace Colors {
|
||||||
constexpr const char* RESET = "\x1b[0m";
|
constexpr const char* RESET = "\x1b[0m";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define EXPECT_MAX_DELTA(expr, desired, delta) \
|
||||||
|
if (const auto RESULT = expr; std::abs(RESULT - (desired)) > delta) { \
|
||||||
|
NLog::log("{}Failed: {}{}, expected max delta of {}, got delta {} ({} - {}). Source: {}@{}.", Colors::RED, Colors::RESET, #expr, delta, (RESULT - (desired)), RESULT, \
|
||||||
|
desired, __FILE__, __LINE__); \
|
||||||
|
ret = 1; \
|
||||||
|
TESTS_FAILED++; \
|
||||||
|
} else { \
|
||||||
|
NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, (RESULT - (desired))); \
|
||||||
|
TESTS_PASSED++; \
|
||||||
|
}
|
||||||
|
|
||||||
#define EXPECT(expr, val) \
|
#define EXPECT(expr, val) \
|
||||||
if (const auto RESULT = expr; RESULT != (val)) { \
|
if (const auto RESULT = expr; RESULT != (val)) { \
|
||||||
NLog::log("{}Failed: {}{}, expected {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \
|
NLog::log("{}Failed: {}{}, expected {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \
|
||||||
|
|
@ -28,6 +39,16 @@ namespace Colors {
|
||||||
TESTS_PASSED++; \
|
TESTS_PASSED++; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EXPECT_NOT(expr, val) \
|
||||||
|
if (const auto RESULT = expr; RESULT == (val)) { \
|
||||||
|
NLog::log("{}Failed: {}{}, expected not {}, 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) \
|
#define EXPECT_VECTOR2D(expr, val) \
|
||||||
do { \
|
do { \
|
||||||
const auto& RESULT = expr; \
|
const auto& RESULT = expr; \
|
||||||
|
|
|
||||||
151
hyprtester/src/tests/clients/child-window.cpp
Normal file
151
hyprtester/src/tests/clients/child-window.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
#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 <unistd.h>
|
||||||
|
#include <csignal>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace Hyprutils::OS;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
#define SP CSharedPointer
|
||||||
|
|
||||||
|
struct SClient {
|
||||||
|
SP<CProcess> proc;
|
||||||
|
std::array<char, 1024> readBuf;
|
||||||
|
CFileDescriptor readFd, writeFd;
|
||||||
|
struct pollfd fds;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ret = 0;
|
||||||
|
|
||||||
|
static bool waitForWindow(SP<CProcess> proc, int windowsBefore) {
|
||||||
|
int counter = 0;
|
||||||
|
while (Tests::processAlive(proc->pid()) && Tests::windowCount() == windowsBefore) {
|
||||||
|
counter++;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
if (counter > 50)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Waited {} milliseconds for window to open", Colors::YELLOW, counter * 100);
|
||||||
|
return Tests::processAlive(proc->pid());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool startClient(SClient& client) {
|
||||||
|
NLog::log("{}Attempting to start child-window client", Colors::YELLOW);
|
||||||
|
|
||||||
|
client.proc = makeShared<CProcess>(binaryDir + "/child-window", std::vector<std::string>{});
|
||||||
|
|
||||||
|
client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
||||||
|
|
||||||
|
int procInPipeFd[2], procOutPipeFd[2];
|
||||||
|
if (pipe(procInPipeFd) != 0 || pipe(procOutPipeFd) != 0) {
|
||||||
|
NLog::log("{}Unable to open pipe to client", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.writeFd = CFileDescriptor(procInPipeFd[1]);
|
||||||
|
client.proc->setStdinFD(procInPipeFd[0]);
|
||||||
|
|
||||||
|
client.readFd = CFileDescriptor(procOutPipeFd[0]);
|
||||||
|
client.proc->setStdoutFD(procOutPipeFd[1]);
|
||||||
|
|
||||||
|
if (!client.proc->runAsync()) {
|
||||||
|
NLog::log("{}Failed to run client", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(procInPipeFd[0]);
|
||||||
|
close(procOutPipeFd[1]);
|
||||||
|
|
||||||
|
if (!waitForWindow(client.proc, Tests::windowCount())) {
|
||||||
|
NLog::log("{}Window took too long to open", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Started child-window client", Colors::YELLOW);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stopClient(SClient& client) {
|
||||||
|
std::string cmd = "exit\n";
|
||||||
|
write(client.writeFd.get(), cmd.c_str(), cmd.length());
|
||||||
|
|
||||||
|
kill(client.proc->pid(), SIGKILL);
|
||||||
|
client.proc.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool createChild(SClient& client) {
|
||||||
|
std::string cmd = "toplevel\n";
|
||||||
|
if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!waitForWindow(client.proc, Tests::windowCount()))
|
||||||
|
NLog::log("{}Child window took too long to open", Colors::RED);
|
||||||
|
|
||||||
|
if (getFromSocket("/dispatch focuswindow class:child-test-child") != "ok") {
|
||||||
|
NLog::log("{}Failed to focus child window", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test() {
|
||||||
|
SClient client;
|
||||||
|
|
||||||
|
if (!startClient(client))
|
||||||
|
return false;
|
||||||
|
OK(getFromSocket("/dispatch setfloating class:child-test-parent"));
|
||||||
|
OK(getFromSocket("/dispatch pin class:child-test-parent"));
|
||||||
|
|
||||||
|
createChild(client);
|
||||||
|
EXPECT(Tests::windowCount(), 2)
|
||||||
|
EXPECT_COUNT_STRING(getFromSocket("/clients"), "pinned: 1", 2);
|
||||||
|
|
||||||
|
stopClient(client);
|
||||||
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
// test that child windows (shouldBeFloated) are not auto-grouped
|
||||||
|
NLog::log("{}Test child windows are not auto-grouped", Colors::GREEN);
|
||||||
|
auto kitty = Tests::spawnKitty();
|
||||||
|
if (!kitty) {
|
||||||
|
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create group and enable auto-grouping
|
||||||
|
OK(getFromSocket("/dispatch togglegroup"));
|
||||||
|
OK(getFromSocket("/keyword group:auto_group true"));
|
||||||
|
|
||||||
|
SClient client2;
|
||||||
|
if (!startClient(client2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
EXPECT(Tests::windowCount(), 2);
|
||||||
|
createChild(client2);
|
||||||
|
EXPECT(Tests::windowCount(), 3);
|
||||||
|
|
||||||
|
// child has set_parent so shouldBeFloated returns true, it should not be auto-grouped
|
||||||
|
EXPECT_COUNT_STRING(getFromSocket("/clients"), "grouped: 0", 1);
|
||||||
|
|
||||||
|
stopClient(client2);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
return !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_CLIENT_TEST_FN(test);
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <hyprutils/os/Process.hpp>
|
#include <hyprutils/os/Process.hpp>
|
||||||
|
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
@ -42,6 +43,7 @@ static bool startClient(SClient& client) {
|
||||||
client.readFd = CFileDescriptor(pipeFds2[0]);
|
client.readFd = CFileDescriptor(pipeFds2[0]);
|
||||||
client.proc->setStdoutFD(pipeFds2[1]);
|
client.proc->setStdoutFD(pipeFds2[1]);
|
||||||
|
|
||||||
|
const int COUNT_BEFORE = Tests::windowCount();
|
||||||
client.proc->runAsync();
|
client.proc->runAsync();
|
||||||
|
|
||||||
close(pipeFds1[0]);
|
close(pipeFds1[0]);
|
||||||
|
|
@ -62,9 +64,18 @@ static bool startClient(SClient& client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for window to appear
|
// wait for window to appear
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
|
int counter = 0;
|
||||||
|
while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) {
|
||||||
|
counter++;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") {
|
if (counter > 50) {
|
||||||
|
NLog::log("{}pointer-scroll client took too long to open", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") {
|
||||||
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
|
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -130,7 +141,7 @@ static bool test() {
|
||||||
EXPECT(sendScroll(10), true);
|
EXPECT(sendScroll(10), true);
|
||||||
EXPECT(getLastDelta(client), 30);
|
EXPECT(getLastDelta(client), 30);
|
||||||
|
|
||||||
EXPECT(getFromSocket("r/dispatch setprop active scrollmouse 4"), "ok");
|
EXPECT(getFromSocket("r/dispatch setprop active scroll_mouse 4"), "ok");
|
||||||
EXPECT(sendScroll(10), true);
|
EXPECT(sendScroll(10), true);
|
||||||
EXPECT(getLastDelta(client), 40);
|
EXPECT(getLastDelta(client), 40);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <hyprutils/os/Process.hpp>
|
#include <hyprutils/os/Process.hpp>
|
||||||
|
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
@ -42,6 +43,7 @@ static bool startClient(SClient& client) {
|
||||||
client.readFd = CFileDescriptor(pipeFds2[0]);
|
client.readFd = CFileDescriptor(pipeFds2[0]);
|
||||||
client.proc->setStdoutFD(pipeFds2[1]);
|
client.proc->setStdoutFD(pipeFds2[1]);
|
||||||
|
|
||||||
|
const int COUNT_BEFORE = Tests::windowCount();
|
||||||
client.proc->runAsync();
|
client.proc->runAsync();
|
||||||
|
|
||||||
close(pipeFds1[0]);
|
close(pipeFds1[0]);
|
||||||
|
|
@ -62,9 +64,18 @@ static bool startClient(SClient& client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for window to appear
|
// wait for window to appear
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
|
int counter = 0;
|
||||||
|
while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) {
|
||||||
|
counter++;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
if (getFromSocket(std::format("/dispatch setprop pid:{} noanim 1", client.proc->pid())) != "ok") {
|
if (counter > 50) {
|
||||||
|
NLog::log("{}pointer-warp client took too long to open", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getFromSocket(std::format("/dispatch setprop pid:{} no_anim 1", client.proc->pid())) != "ok") {
|
||||||
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
|
NLog::log("{}Failed to disable animations for client window", Colors::RED, ret);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
180
hyprtester/src/tests/clients/shortcut-inhibitor.cpp
Normal file
180
hyprtester/src/tests/clients/shortcut-inhibitor.cpp
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
#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>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
using namespace Hyprutils::OS;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
#define SP CSharedPointer
|
||||||
|
|
||||||
|
struct SClient {
|
||||||
|
SP<CProcess> proc;
|
||||||
|
std::array<char, 1024> readBuf;
|
||||||
|
CFileDescriptor readFd, writeFd;
|
||||||
|
struct pollfd fds;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ret = 0;
|
||||||
|
|
||||||
|
static bool startClient(SClient& client) {
|
||||||
|
Tests::killAllWindows();
|
||||||
|
client.proc = makeShared<CProcess>(binaryDir + "/shortcut-inhibitor", 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]);
|
||||||
|
|
||||||
|
const int COUNT_BEFORE = Tests::windowCount();
|
||||||
|
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)) {
|
||||||
|
NLog::log("{}shortcut-inhibitor client failed poll", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.readBuf.fill(0);
|
||||||
|
if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) {
|
||||||
|
NLog::log("{}shortcut-inhibitor client read failed", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ret = std::string{client.readBuf.data()};
|
||||||
|
if (ret.find("started") == std::string::npos) {
|
||||||
|
NLog::log("{}Failed to start shortcut-inhibitor client, read {}", Colors::RED, ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for window to appear
|
||||||
|
int counter = 0;
|
||||||
|
while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) {
|
||||||
|
counter++;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
if (counter > 50) {
|
||||||
|
NLog::log("{}shortcut-inhibitor client took too long to open", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Tests::processAlive(client.proc->pid())) {
|
||||||
|
NLog::log("{}shortcut-inhibitor client not alive", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getFromSocket(std::format("/dispatch focuswindow pid:{}", client.proc->pid())) != "ok") {
|
||||||
|
NLog::log("{}Failed to focus shortcut-inhibitor client", Colors::RED, ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string command = "on\n";
|
||||||
|
if (write(client.writeFd.get(), command.c_str(), command.length()) == -1) {
|
||||||
|
NLog::log("{}shortcut-inhibitor client write failed", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.readBuf.fill(0);
|
||||||
|
if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ret = std::string{client.readBuf.data()};
|
||||||
|
if (ret.find("inhibiting") == std::string::npos) {
|
||||||
|
NLog::log("{}shortcut-inhibitor client didn't return inhibiting", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Started shortcut-inhibitor client", Colors::YELLOW);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stopClient(SClient& client) {
|
||||||
|
std::string cmd = "off\n";
|
||||||
|
write(client.writeFd.get(), cmd.c_str(), cmd.length());
|
||||||
|
|
||||||
|
kill(client.proc->pid(), SIGKILL);
|
||||||
|
client.proc.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string flagFile = "/tmp/hyprtester-keybinds.txt";
|
||||||
|
|
||||||
|
static bool checkFlag() {
|
||||||
|
bool exists = std::filesystem::exists(flagFile);
|
||||||
|
std::filesystem::remove(flagFile);
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool attemptCheckFlag(int attempts, int intervalMs) {
|
||||||
|
for (int i = 0; i < attempts; i++) {
|
||||||
|
if (checkFlag())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test() {
|
||||||
|
SClient client;
|
||||||
|
if (!startClient(client))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NLog::log("{}Testing keybinds", Colors::GREEN);
|
||||||
|
//basic keybind test
|
||||||
|
EXPECT(checkFlag(), false);
|
||||||
|
EXPECT(getFromSocket("/keyword bind SUPER,Y,exec,touch " + flagFile), "ok");
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
|
EXPECT(attemptCheckFlag(20, 50), false);
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
|
|
||||||
|
//keybind bypass flag test
|
||||||
|
EXPECT(checkFlag(), false);
|
||||||
|
EXPECT(getFromSocket("/keyword bindp SUPER,Y,exec,touch " + flagFile), "ok");
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
|
EXPECT(attemptCheckFlag(20, 50), true);
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
|
|
||||||
|
NLog::log("{}Testing gestures", Colors::GREEN);
|
||||||
|
//basic gesture test
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:gesture right,3"));
|
||||||
|
EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "floating: 1");
|
||||||
|
|
||||||
|
//gesture bypass flag test
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:gesture right,2"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "floating: 1");
|
||||||
|
|
||||||
|
stopClient(client);
|
||||||
|
|
||||||
|
NLog::log("{}Reloading the config", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
return !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_CLIENT_TEST_FN(test);
|
||||||
|
|
@ -16,6 +16,7 @@ static void testFloatClamp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
OK(getFromSocket("/keyword dwindle:force_split 2"));
|
OK(getFromSocket("/keyword dwindle:force_split 2"));
|
||||||
|
OK(getFromSocket("/keyword monitor HEADLESS-2, addreserved, 0, 20, 0, 20"));
|
||||||
OK(getFromSocket("/dispatch focuswindow class:c"));
|
OK(getFromSocket("/dispatch focuswindow class:c"));
|
||||||
OK(getFromSocket("/dispatch setfloating class:c"));
|
OK(getFromSocket("/dispatch setfloating class:c"));
|
||||||
OK(getFromSocket("/dispatch resizewindowpixel exact 1200 900,class:c"));
|
OK(getFromSocket("/dispatch resizewindowpixel exact 1200 900,class:c"));
|
||||||
|
|
@ -24,7 +25,7 @@ static void testFloatClamp() {
|
||||||
|
|
||||||
{
|
{
|
||||||
auto str = getFromSocket("/clients");
|
auto str = getFromSocket("/clients");
|
||||||
EXPECT_CONTAINS(str, "at: 718,178");
|
EXPECT_CONTAINS(str, "at: 698,158");
|
||||||
EXPECT_CONTAINS(str, "size: 1200,900");
|
EXPECT_CONTAINS(str, "size: 1200,900");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,6 +34,197 @@ static void testFloatClamp() {
|
||||||
// clean up
|
// clean up
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test13349() {
|
||||||
|
|
||||||
|
// Test if dwindle properly uses a focal point to place a new window.
|
||||||
|
// exposed by #13349 as a regression from #12890
|
||||||
|
|
||||||
|
for (auto const& win : {"a", "b", "c"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:c"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 967,547");
|
||||||
|
EXPECT_CONTAINS(str, "size: 931,511");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movewindow l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 497,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 456,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movewindow r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 967,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 456,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSplit() {
|
||||||
|
// Test various split methods
|
||||||
|
|
||||||
|
Tests::spawnKitty("a");
|
||||||
|
|
||||||
|
// these must not crash
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch layoutmsg swapsplit"), "ok");
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio 1 exact"), "ok");
|
||||||
|
|
||||||
|
Tests::spawnKitty("b");
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg splitratio -0.2"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 743,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg splitratio 1.6 exact"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1495,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio fhne exact"), "ok");
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio exact"), "ok");
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio -....9"), "ok");
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio ..9"), "ok");
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch layoutmsg splitratio"), "ok");
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg togglesplit"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876,823");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg swapsplit"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,859");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876,199");
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testRotatesplit() {
|
||||||
|
OK(getFromSocket("r/keyword general:gaps_in 0"));
|
||||||
|
OK(getFromSocket("r/keyword general:gaps_out 0"));
|
||||||
|
OK(getFromSocket("r/keyword general:border_size 0"));
|
||||||
|
|
||||||
|
for (auto const& win : {"a", "b"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 0,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 960,1080");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test 4 repeated rotations by 90 degrees
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 0,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1920,540");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 960,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 960,1080");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 0,540");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1920,540");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 0,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 960,1080");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test different angles
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit 180"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 960,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 960,1080");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit 270"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 0,540");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1920,540");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit 360"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 0,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1920,540");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test negative angles
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit -90"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 0,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 960,1080");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg rotatesplit -180"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_CONTAINS(str, "at: 960,0");
|
||||||
|
EXPECT_CONTAINS(str, "size: 960,1080");
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
|
|
@ -42,6 +234,15 @@ static bool test() {
|
||||||
NLog::log("{}Testing float clamp", Colors::GREEN);
|
NLog::log("{}Testing float clamp", Colors::GREEN);
|
||||||
testFloatClamp();
|
testFloatClamp();
|
||||||
|
|
||||||
|
NLog::log("{}Testing #13349", Colors::GREEN);
|
||||||
|
test13349();
|
||||||
|
|
||||||
|
NLog::log("{}Testing splits", Colors::GREEN);
|
||||||
|
testSplit();
|
||||||
|
|
||||||
|
NLog::log("{}Testing rotatesplit", Colors::GREEN);
|
||||||
|
testRotatesplit();
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
NLog::log("Cleaning up", Colors::YELLOW);
|
NLog::log("Cleaning up", Colors::YELLOW);
|
||||||
getFromSocket("/dispatch workspace 1");
|
getFromSocket("/dispatch workspace 1");
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "../../shared.hpp"
|
#include "../../shared.hpp"
|
||||||
#include "../../hyprctlCompat.hpp"
|
#include "../../hyprctlCompat.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <format>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <hyprutils/os/Process.hpp>
|
#include <hyprutils/os/Process.hpp>
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
|
|
@ -15,12 +16,15 @@ using namespace Hyprutils::Memory;
|
||||||
#define UP CUniquePointer
|
#define UP CUniquePointer
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
|
|
||||||
|
const static auto SLEEP_DURATIONS = std::array{1, 10};
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing process spawning", Colors::GREEN);
|
NLog::log("{}Testing process spawning", Colors::GREEN);
|
||||||
|
|
||||||
|
for (const auto duration : SLEEP_DURATIONS) {
|
||||||
// Note: POSIX sleep does not support fractional seconds, so
|
// Note: POSIX sleep does not support fractional seconds, so
|
||||||
// can't sleep for less than 1 second.
|
// can't sleep for less than 1 second.
|
||||||
OK(getFromSocket("/dispatch exec sleep 1"));
|
OK(getFromSocket(std::format("/dispatch exec sleep {}", duration)));
|
||||||
|
|
||||||
// Ensure that sleep is our child
|
// Ensure that sleep is our child
|
||||||
const std::string sleepPidS = Tests::execAndGet("pgrep sleep");
|
const std::string sleepPidS = Tests::execAndGet("pgrep sleep");
|
||||||
|
|
@ -29,14 +33,14 @@ static bool test() {
|
||||||
sleepPid = std::stoull(sleepPidS);
|
sleepPid = std::stoull(sleepPidS);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
NLog::log("{}Sleep was not spawned or several sleeps are running: pgrep returned '{}'", Colors::RED, sleepPidS);
|
NLog::log("{}Sleep was not spawned or several sleeps are running: pgrep returned '{}'", Colors::RED, sleepPidS);
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string sleepParentComm = Tests::execAndGet("cat \"/proc/$(ps -o ppid:1= -p " + sleepPidS + ")/comm\"");
|
const std::string sleepParentComm = Tests::execAndGet("cat \"/proc/$(ps -o ppid:1= -p " + sleepPidS + ")/comm\"");
|
||||||
NLog::log("{}Expecting that sleep's parent is Hyprland", Colors::YELLOW);
|
NLog::log("{}Expecting that sleep's parent is Hyprland", Colors::YELLOW);
|
||||||
EXPECT_CONTAINS(sleepParentComm, "Hyprland");
|
EXPECT_CONTAINS(sleepParentComm, "Hyprland");
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(duration));
|
||||||
|
|
||||||
// Ensure that sleep did not become a zombie
|
// Ensure that sleep did not become a zombie
|
||||||
EXPECT(Tests::processAlive(sleepPid), false);
|
EXPECT(Tests::processAlive(sleepPid), false);
|
||||||
|
|
@ -49,6 +53,9 @@ static bool test() {
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
return !ret;
|
return !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_TEST_FN(test)
|
REGISTER_TEST_FN(test)
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,33 @@ static bool test() {
|
||||||
// The cursor should have moved because of the gesture
|
// The cursor should have moved because of the gesture
|
||||||
EXPECT(cursorPos1 != cursorPos2, true);
|
EXPECT(cursorPos1 != cursorPos2, true);
|
||||||
|
|
||||||
|
// Test that `workspace previous` works correctly after a workspace gesture.
|
||||||
|
{
|
||||||
|
OK(getFromSocket("/keyword gestures:workspace_swipe_invert 0"));
|
||||||
|
OK(getFromSocket("/keyword gestures:workspace_swipe_create_new 1"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 3"));
|
||||||
|
|
||||||
|
// Come to workspace 5 from workspace 3: 5 will remember that.
|
||||||
|
OK(getFromSocket("/dispatch workspace 5"));
|
||||||
|
Tests::spawnKitty(); // Keep workspace 5 open
|
||||||
|
|
||||||
|
// Swipe from 1 to 5: 5 shall remember that.
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:alt 1"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:gesture right,3"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:alt 0"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 5 (5)");
|
||||||
|
|
||||||
|
// Must return to 1 rather than 3
|
||||||
|
OK(getFromSocket("/dispatch workspace previous"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 1 (1)");
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace previous"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activeworkspace"), "ID 5 (5)");
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
}
|
||||||
|
|
||||||
// kill all
|
// kill all
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,34 @@ static bool test() {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test movegroupwindow: focus should follow the moved window
|
||||||
|
NLog::log("{}Test movegroupwindow focus follows window", Colors::YELLOW);
|
||||||
|
try {
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
||||||
|
OK(getFromSocket("/dispatch movegroupwindow f"));
|
||||||
|
str = getFromSocket("/activewindow");
|
||||||
|
auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
||||||
|
EXPECT(activeAfterMove, activeBeforeMove);
|
||||||
|
} catch (...) {
|
||||||
|
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// and backwards
|
||||||
|
NLog::log("{}Test movegroupwindow backwards", Colors::YELLOW);
|
||||||
|
try {
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
||||||
|
OK(getFromSocket("/dispatch movegroupwindow b"));
|
||||||
|
str = getFromSocket("/activewindow");
|
||||||
|
auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16);
|
||||||
|
EXPECT(activeAfterMove, activeBeforeMove);
|
||||||
|
} catch (...) {
|
||||||
|
NLog::log("{}Fail at getting prop", Colors::RED);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
NLog::log("{}Disable autogrouping", Colors::YELLOW);
|
NLog::log("{}Disable autogrouping", Colors::YELLOW);
|
||||||
OK(getFromSocket("/keyword group:auto_group false"));
|
OK(getFromSocket("/keyword group:auto_group false"));
|
||||||
|
|
||||||
|
|
@ -173,6 +201,99 @@ static bool test() {
|
||||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
// test movewindoworgroup: direction should be respected when extracting from group
|
||||||
|
NLog::log("{}Test movewindoworgroup respects direction out of group", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/keyword group:groupbar:enabled 0"));
|
||||||
|
{
|
||||||
|
auto kittyE = Tests::spawnKitty();
|
||||||
|
if (!kittyE) {
|
||||||
|
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// group kitty, and new windows should be auto-grouped
|
||||||
|
OK(getFromSocket("/dispatch togglegroup"));
|
||||||
|
|
||||||
|
auto kittyF = Tests::spawnKitty();
|
||||||
|
if (!kittyF) {
|
||||||
|
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPECT(Tests::windowCount(), 2);
|
||||||
|
|
||||||
|
// both windows should be grouped at the same position
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_COUNT_STRING(str, "at: 22,22", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move active window out of group to the right
|
||||||
|
NLog::log("{}Test movewindoworgroup r", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch movewindoworgroup r"));
|
||||||
|
|
||||||
|
// the group should stay at x=22, the extracted window should be to the right
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_COUNT_STRING(str, "at: 22,22", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move it back into the group
|
||||||
|
OK(getFromSocket("/dispatch moveintogroup l"));
|
||||||
|
|
||||||
|
// move active window out of group downward
|
||||||
|
NLog::log("{}Test movewindoworgroup d", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch movewindoworgroup d"));
|
||||||
|
|
||||||
|
// the group should stay at y=22, the extracted window should be below
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT_COUNT_STRING(str, "at: 22,22", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that we deny a floated window getting auto-grouped into a tiled group.
|
||||||
|
NLog::log("{}Test that we deny a floated window getting auto-grouped into a tiled group.", Colors::GREEN);
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-tiled]:match:class kitty_tiled"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-tiled]:tile yes"));
|
||||||
|
auto kittyProcE = Tests::spawnKitty("kitty_tiled");
|
||||||
|
if (!kittyProcE) {
|
||||||
|
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
OK(getFromSocket("/dispatch togglegroup"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-floated]:match:class kitty_floated"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-floated]:float yes"));
|
||||||
|
auto kittyProcF = Tests::spawnKitty("kitty_floated");
|
||||||
|
if (!kittyProcF) {
|
||||||
|
NLog::log("{}Error: kitty did not spawn", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT(Tests::windowCount(), 2);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto clients = getFromSocket("/clients");
|
||||||
|
auto classPos = clients.find("class: kitty_floated");
|
||||||
|
if (classPos == std::string::npos) {
|
||||||
|
NLog::log("{}Could not find kitty_floated in clients output", Colors::RED);
|
||||||
|
ret = 1;
|
||||||
|
} else {
|
||||||
|
auto entryStart = clients.rfind("Window ", classPos);
|
||||||
|
auto entryEnd = clients.find("\n\n", classPos);
|
||||||
|
auto windowEntry = clients.substr(entryStart, entryEnd - entryStart);
|
||||||
|
EXPECT_CONTAINS(windowEntry, "floating: 1");
|
||||||
|
EXPECT_CONTAINS(windowEntry, "grouped: 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
return !ret;
|
return !ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,82 +56,92 @@ static bool testGetprop() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// animationstyle
|
// animation
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "(unset)");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "(unset)");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": ""})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": ""})");
|
||||||
getFromSocket("/dispatch setprop class:kitty animationstyle teststyle");
|
getFromSocket("/dispatch setprop class:kitty animation teststyle");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle"), "teststyle");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation"), "teststyle");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animationstyle -j"), R"({"animationstyle": "teststyle"})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty animation -j"), R"({"animation": "teststyle"})");
|
||||||
|
|
||||||
// maxsize
|
// max_size
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "inf inf");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "inf inf");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [null,null]})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [null,null]})");
|
||||||
getFromSocket("/dispatch setprop class:kitty maxsize 200 150");
|
getFromSocket("/dispatch setprop class:kitty max_size 200 150");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize"), "200 150");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "200 150");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty maxsize -j"), R"({"maxsize": [200,150]})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [200,150]})");
|
||||||
|
|
||||||
// minsize
|
// min_size
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "20 20");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "20 20");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [20,20]})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [20,20]})");
|
||||||
getFromSocket("/dispatch setprop class:kitty minsize 100 50");
|
getFromSocket("/dispatch setprop class:kitty min_size 100 50");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize"), "100 50");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "100 50");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty minsize -j"), R"({"minsize": [100,50]})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [100,50]})");
|
||||||
|
|
||||||
// alpha
|
// expr-based min/max _size
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "1");
|
getFromSocket("/dispatch setfloating class:kitty"); // need to set floating for tests below
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 1})");
|
getFromSocket("/dispatch setprop class:kitty max_size 90+10 25*2"); // set max to the same as min above, forcing window to 100*50
|
||||||
getFromSocket("/dispatch setprop class:kitty alpha 0.3");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size"), "100 50");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha"), "0.3");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty max_size -j"), R"({"max_size": [100,50]})");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alpha -j"), R"({"alpha": 0.3})");
|
getFromSocket("/dispatch setprop class:kitty min_size window_w*0.5 window_h-10");
|
||||||
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size"), "50 40");
|
||||||
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty min_size -j"), R"({"min_size": [50,40]})");
|
||||||
|
getFromSocket("/dispatch settiled class:kitty"); // go back to tiled for consistency
|
||||||
|
|
||||||
// alphainactive
|
// opacity
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "1");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "1");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 1})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 1})");
|
||||||
getFromSocket("/dispatch setprop class:kitty alphainactive 0.5");
|
getFromSocket("/dispatch setprop class:kitty opacity 0.3");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive"), "0.5");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity"), "0.3");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactive -j"), R"({"alphainactive": 0.5})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity -j"), R"({"opacity": 0.3})");
|
||||||
|
|
||||||
// alphafullscreen
|
// opacity_inactive
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "1");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "1");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 1})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 1})");
|
||||||
getFromSocket("/dispatch setprop class:kitty alphafullscreen 0.75");
|
getFromSocket("/dispatch setprop class:kitty opacity_inactive 0.5");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen"), "0.75");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive"), "0.5");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreen -j"), R"({"alphafullscreen": 0.75})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive -j"), R"({"opacity_inactive": 0.5})");
|
||||||
|
|
||||||
// alphaoverride
|
// opacity_fullscreen
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "false");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "1");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": false})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 1})");
|
||||||
getFromSocket("/dispatch setprop class:kitty alphaoverride true");
|
getFromSocket("/dispatch setprop class:kitty opacity_fullscreen 0.75");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride"), "true");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen"), "0.75");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphaoverride -j"), R"({"alphaoverride": true})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen -j"), R"({"opacity_fullscreen": 0.75})");
|
||||||
|
|
||||||
// alphainactiveoverride
|
// opacity_override
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "false");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "false");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": false})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": false})");
|
||||||
getFromSocket("/dispatch setprop class:kitty alphainactiveoverride true");
|
getFromSocket("/dispatch setprop class:kitty opacity_override true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride"), "true");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override"), "true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphainactiveoverride -j"), R"({"alphainactiveoverride": true})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_override -j"), R"({"opacity_override": true})");
|
||||||
|
|
||||||
// alphafullscreenoverride
|
// opacity_inactive_override
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "false");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "false");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": false})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": false})");
|
||||||
getFromSocket("/dispatch setprop class:kitty alphafullscreenoverride true");
|
getFromSocket("/dispatch setprop class:kitty opacity_inactive_override true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride"), "true");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override"), "true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty alphafullscreenoverride -j"), R"({"alphafullscreenoverride": true})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_inactive_override -j"), R"({"opacity_inactive_override": true})");
|
||||||
|
|
||||||
// activebordercolor
|
// opacity_fullscreen_override
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ee33ccff ee00ff99 45deg");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "false");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ee33ccff ee00ff99 45deg"})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": false})");
|
||||||
getFromSocket("/dispatch setprop class:kitty activebordercolor rgb(abcdef)");
|
getFromSocket("/dispatch setprop class:kitty opacity_fullscreen_override true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor"), "ffabcdef 0deg");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override"), "true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty activebordercolor -j"), R"({"activebordercolor": "ffabcdef 0deg"})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty opacity_fullscreen_override -j"), R"({"opacity_fullscreen_override": true})");
|
||||||
|
|
||||||
|
// active_border_color
|
||||||
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ee33ccff ee00ff99 45deg");
|
||||||
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ee33ccff ee00ff99 45deg"})");
|
||||||
|
getFromSocket("/dispatch setprop class:kitty active_border_color rgb(abcdef)");
|
||||||
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color"), "ffabcdef 0deg");
|
||||||
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty active_border_color -j"), R"({"active_border_color": "ffabcdef 0deg"})");
|
||||||
|
|
||||||
// bool window properties
|
// bool window properties
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "false");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "false");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": false})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": false})");
|
||||||
getFromSocket("/dispatch setprop class:kitty allowsinput true");
|
getFromSocket("/dispatch setprop class:kitty allows_input true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput"), "true");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input"), "true");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allowsinput -j"), R"({"allowsinput": true})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty allows_input -j"), R"({"allows_input": true})");
|
||||||
|
|
||||||
// int window properties
|
// int window properties
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "10");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding"), "10");
|
||||||
|
|
@ -141,16 +151,16 @@ static bool testGetprop() {
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding -j"), R"({"rounding": 4})");
|
||||||
|
|
||||||
// float window properties
|
// float window properties
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "2");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "2");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 2})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 2})");
|
||||||
getFromSocket("/dispatch setprop class:kitty roundingpower 1.25");
|
getFromSocket("/dispatch setprop class:kitty rounding_power 1.25");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower"), "1.25");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power"), "1.25");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty roundingpower -j"), R"({"roundingpower": 1.25})");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty rounding_power -j"), R"({"rounding_power": 1.25})");
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args");
|
EXPECT(getCommandStdOut("hyprctl getprop"), "not enough args");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animationstyle"), "window not found");
|
EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animation"), "window not found");
|
||||||
EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found");
|
EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found");
|
||||||
|
|
||||||
// kill all
|
// kill all
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,17 @@ static bool checkFlag() {
|
||||||
return exists;
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool attemptCheckFlag(int attempts, int intervalMs) {
|
||||||
|
for (int i = 0; i < attempts; i++) {
|
||||||
|
if (checkFlag())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static std::string readKittyOutput() {
|
static std::string readKittyOutput() {
|
||||||
std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all");
|
std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all");
|
||||||
// chop off shell prompt
|
// chop off shell prompt
|
||||||
|
|
@ -75,8 +86,7 @@ static void testBind() {
|
||||||
// press keybind
|
// press keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
// await flag
|
// await flag
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
EXPECT(attemptCheckFlag(20, 50), true);
|
||||||
EXPECT(checkFlag(), true);
|
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
|
|
@ -88,8 +98,7 @@ static void testBindKey() {
|
||||||
// press keybind
|
// press keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29"));
|
||||||
// await flag
|
// await flag
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
EXPECT(attemptCheckFlag(20, 50), true);
|
||||||
EXPECT(checkFlag(), true);
|
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
EXPECT(getFromSocket("/keyword unbind ,Y"), "ok");
|
EXPECT(getFromSocket("/keyword unbind ,Y"), "ok");
|
||||||
|
|
@ -105,7 +114,7 @@ static void testLongPress() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
|
|
@ -122,7 +131,7 @@ static void testKeyLongPress() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
|
|
@ -141,7 +150,7 @@ static void testLongPressRelease() {
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +167,7 @@ static void testLongPressOnlyKeyRelease() {
|
||||||
// release key, keep modifier
|
// release key, keep modifier
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
|
|
@ -171,13 +180,13 @@ static void testRepeat() {
|
||||||
// press keybind
|
// press keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
// await flag
|
// await flag
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// check that it continues repeating
|
// check that it continues repeating
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
|
|
@ -194,10 +203,10 @@ static void testKeyRepeat() {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// check that it continues repeating
|
// check that it continues repeating
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
|
|
@ -216,10 +225,12 @@ static void testRepeatRelease() {
|
||||||
// release keybind
|
// release keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
clearFlag();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
// check that it is not repeating
|
// check that it is not repeating
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
}
|
}
|
||||||
|
|
@ -231,15 +242,17 @@ static void testRepeatOnlyKeyRelease() {
|
||||||
// press keybind
|
// press keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
// await flag
|
// await flag
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), true);
|
EXPECT(checkFlag(), true);
|
||||||
// release key, keep modifier
|
// release key, keep modifier
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
|
clearFlag();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
// check that it is not repeating
|
// check that it is not repeating
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
EXPECT(checkFlag(), false);
|
EXPECT(checkFlag(), false);
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
|
|
@ -304,9 +317,9 @@ static void testShortcutLongPress() {
|
||||||
// press keybind
|
// press keybind
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
const std::string output = readKittyOutput();
|
const std::string output = readKittyOutput();
|
||||||
int yCount = Tests::countOccurrences(output, "y");
|
int yCount = Tests::countOccurrences(output, "y");
|
||||||
// sometimes 1, sometimes 2, not sure why
|
// sometimes 1, sometimes 2, not sure why
|
||||||
|
|
@ -336,7 +349,7 @@ static void testShortcutLongPressKeyRelease() {
|
||||||
// release key, keep modifier
|
// release key, keep modifier
|
||||||
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||||
// await repeat delay
|
// await repeat delay
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(150));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
const std::string output = readKittyOutput();
|
const std::string output = readKittyOutput();
|
||||||
// disabled: doesn't work on CI
|
// disabled: doesn't work on CI
|
||||||
// EXPECT_COUNT_STRING(output, "y", 1);
|
// EXPECT_COUNT_STRING(output, "y", 1);
|
||||||
|
|
@ -443,9 +456,68 @@ static void testSubmap() {
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testBindsAfterScroll() {
|
||||||
|
NLog::log("{}Testing binds after scroll", Colors::GREEN);
|
||||||
|
|
||||||
|
clearFlag();
|
||||||
|
OK(getFromSocket("/keyword binds Alt_R,w,exec,touch " + flagFile));
|
||||||
|
|
||||||
|
// press keybind before scroll
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,108")); // Alt_R press
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,4,25")); // w press
|
||||||
|
EXPECT(attemptCheckFlag(20, 50), true);
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,4,25")); // w release
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,108")); // Alt_R release
|
||||||
|
|
||||||
|
// scroll
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:scroll 120"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:scroll -120"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:scroll 120"));
|
||||||
|
|
||||||
|
// press keybind after scroll
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,108")); // Alt_R press
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,4,25")); // w press
|
||||||
|
EXPECT(attemptCheckFlag(20, 50), true);
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,4,25")); // w release
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,108")); // Alt_R release
|
||||||
|
|
||||||
|
clearFlag();
|
||||||
|
OK(getFromSocket("/keyword unbind Alt_R,w"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSubmapUniversal() {
|
||||||
|
NLog::log("{}Testing submap universal", Colors::GREEN);
|
||||||
|
|
||||||
|
EXPECT(checkFlag(), false);
|
||||||
|
EXPECT(getFromSocket("/keyword bindu SUPER,Y,exec,touch " + flagFile), "ok");
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/submap"), "default");
|
||||||
|
|
||||||
|
// keybind works on default submap
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||||
|
EXPECT(attemptCheckFlag(30, 5), true);
|
||||||
|
|
||||||
|
// keybind works on submap1
|
||||||
|
getFromSocket("/dispatch plugin:test:keybind 1,7,30");
|
||||||
|
getFromSocket("/dispatch plugin:test:keybind 0,7,30");
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/submap"), "submap1");
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
|
||||||
|
EXPECT(attemptCheckFlag(30, 5), true);
|
||||||
|
|
||||||
|
// reset to default submap
|
||||||
|
getFromSocket("/dispatch plugin:test:keybind 1,0,33");
|
||||||
|
getFromSocket("/dispatch plugin:test:keybind 0,0,33");
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/submap"), "default");
|
||||||
|
|
||||||
|
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
|
||||||
|
}
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing keybinds", Colors::GREEN);
|
NLog::log("{}Testing keybinds", Colors::GREEN);
|
||||||
|
|
||||||
|
clearFlag();
|
||||||
|
|
||||||
testBind();
|
testBind();
|
||||||
testBindKey();
|
testBindKey();
|
||||||
testLongPress();
|
testLongPress();
|
||||||
|
|
@ -462,8 +534,9 @@ static bool test() {
|
||||||
testShortcutLongPressKeyRelease();
|
testShortcutLongPressKeyRelease();
|
||||||
testShortcutRepeat();
|
testShortcutRepeat();
|
||||||
testShortcutRepeatKeyRelease();
|
testShortcutRepeatKeyRelease();
|
||||||
|
|
||||||
testSubmap();
|
testSubmap();
|
||||||
|
testSubmapUniversal();
|
||||||
|
testBindsAfterScroll();
|
||||||
|
|
||||||
clearFlag();
|
clearFlag();
|
||||||
return !ret;
|
return !ret;
|
||||||
|
|
|
||||||
53
hyprtester/src/tests/main/layer.cpp
Normal file
53
hyprtester/src/tests/main/layer.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "../../Log.hpp"
|
||||||
|
#include "../shared.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 spawnLayer(const std::string& namespace_) {
|
||||||
|
NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, namespace_);
|
||||||
|
if (!Tests::spawnLayerKitty(namespace_)) {
|
||||||
|
NLog::log("{}Error: {} layer did not spawn", Colors::RED, namespace_);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test() {
|
||||||
|
NLog::log("{}Testing plugin layerrules", Colors::GREEN);
|
||||||
|
|
||||||
|
if (!spawnLayer("rule-layer"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:add_layer_rule"));
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword layerrule match:namespace rule-layer, plugin_rule effect"));
|
||||||
|
|
||||||
|
if (!spawnLayer("rule-layer"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!spawnLayer("norule-layer"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:check_layer_rule"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
NLog::log("{}Killing all layers", Colors::YELLOW);
|
||||||
|
Tests::killAllLayers();
|
||||||
|
|
||||||
|
NLog::log("{}Expecting 0 layers", Colors::YELLOW);
|
||||||
|
EXPECT(Tests::layerCount(), 0);
|
||||||
|
|
||||||
|
return !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_TEST_FN(test)
|
||||||
127
hyprtester/src/tests/main/layout.cpp
Normal file
127
hyprtester/src/tests/main/layout.cpp
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
#include "../shared.hpp"
|
||||||
|
#include "../../shared.hpp"
|
||||||
|
#include "../../hyprctlCompat.hpp"
|
||||||
|
#include "tests.hpp"
|
||||||
|
|
||||||
|
static int ret = 0;
|
||||||
|
|
||||||
|
static void swar() {
|
||||||
|
OK(getFromSocket("/keyword layout:single_window_aspect_ratio 1 1"));
|
||||||
|
|
||||||
|
Tests::spawnKitty();
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 442,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1036,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::spawnKitty();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch killwindow activewindow"));
|
||||||
|
|
||||||
|
Tests::waitUntilWindowsN(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 442,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1036,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't use swar on maximized
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 1"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't crash when focus after global geometry changes
|
||||||
|
static void testCrashOnGeomUpdate() {
|
||||||
|
Tests::spawnKitty();
|
||||||
|
Tests::spawnKitty();
|
||||||
|
Tests::spawnKitty();
|
||||||
|
|
||||||
|
// move the layout
|
||||||
|
OK(getFromSocket("/keyword monitor HEADLESS-2,1920x1080@60,1000x0,1"));
|
||||||
|
|
||||||
|
// shouldnt crash
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if size + pos is preserved after fs cycle
|
||||||
|
static void testPosPreserve() {
|
||||||
|
Tests::spawnKitty();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch setfloating class:kitty"));
|
||||||
|
OK(getFromSocket("/dispatch resizewindowpixel exact 1337 69, class:kitty"));
|
||||||
|
OK(getFromSocket("/dispatch movewindowpixel exact 420 420, class:kitty"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 420,420");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1337,69");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch fullscreen"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1337,69");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movewindow r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 581,420");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1337,69");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch fullscreen"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 581,420");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1337,69");
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test() {
|
||||||
|
NLog::log("{}Testing layout generic", Colors::GREEN);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
OK(getFromSocket("/dispatch workspace 10"));
|
||||||
|
|
||||||
|
// test
|
||||||
|
NLog::log("{}Testing `single_window_aspect_ratio`", Colors::GREEN);
|
||||||
|
swar();
|
||||||
|
|
||||||
|
testCrashOnGeomUpdate();
|
||||||
|
testPosPreserve();
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("Cleaning up", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
return !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_TEST_FN(test);
|
||||||
|
|
@ -5,6 +5,52 @@
|
||||||
|
|
||||||
static int ret = 0;
|
static int ret = 0;
|
||||||
|
|
||||||
|
// reqs 1 master 3 slaves
|
||||||
|
static void testOrientations() {
|
||||||
|
OK(getFromSocket("/keyword master:orientation top"));
|
||||||
|
|
||||||
|
// top
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876");
|
||||||
|
}
|
||||||
|
|
||||||
|
// cycle = top, right, bottom, center, left
|
||||||
|
|
||||||
|
// right
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg orientationnext"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 873,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1025,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg orientationnext"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,495");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876");
|
||||||
|
}
|
||||||
|
|
||||||
|
// center
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg orientationnext"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 450,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1020,1036");
|
||||||
|
}
|
||||||
|
|
||||||
|
// left
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg orientationnext"));
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1025,1036");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void focusMasterPrevious() {
|
static void focusMasterPrevious() {
|
||||||
// setup
|
// setup
|
||||||
NLog::log("{}Spawning 1 master and 3 slave windows", Colors::YELLOW);
|
NLog::log("{}Spawning 1 master and 3 slave windows", Colors::YELLOW);
|
||||||
|
|
@ -44,11 +90,74 @@ static void focusMasterPrevious() {
|
||||||
OK(getFromSocket("/dispatch layoutmsg focusmaster previous"));
|
OK(getFromSocket("/dispatch layoutmsg focusmaster previous"));
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master");
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master");
|
||||||
|
|
||||||
|
testOrientations();
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testFsBehavior() {
|
||||||
|
// Master will re-send data to fullscreen / maximized windows, which can interfere with misc:on_focus_under_fullscreen
|
||||||
|
// check that it doesn't.
|
||||||
|
|
||||||
|
for (auto const& win : {"master", "slave1", "slave2"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:master"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 1"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876,1036");
|
||||||
|
EXPECT_CONTAINS(str, "class: master");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));
|
||||||
|
|
||||||
|
Tests::spawnKitty("new_master");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876,1036");
|
||||||
|
EXPECT_CONTAINS(str, "class: new_master");
|
||||||
|
EXPECT_CONTAINS(str, "fullscreen: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
||||||
|
|
||||||
|
Tests::spawnKitty("ignored");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "at: 22,22");
|
||||||
|
EXPECT_CONTAINS(str, "size: 1876,1036");
|
||||||
|
EXPECT_CONTAINS(str, "class: new_master");
|
||||||
|
EXPECT_CONTAINS(str, "fullscreen: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));
|
||||||
|
|
||||||
|
Tests::spawnKitty("vaxwashere");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: vaxwashere");
|
||||||
|
EXPECT_CONTAINS(str, "fullscreen: 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing Master layout", Colors::GREEN);
|
NLog::log("{}Testing Master layout", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -60,6 +169,9 @@ static bool test() {
|
||||||
NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN);
|
NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN);
|
||||||
focusMasterPrevious();
|
focusMasterPrevious();
|
||||||
|
|
||||||
|
NLog::log("{}Testing fs behavior", Colors::GREEN);
|
||||||
|
testFsBehavior();
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
NLog::log("Cleaning up", Colors::YELLOW);
|
NLog::log("Cleaning up", Colors::YELLOW);
|
||||||
OK(getFromSocket("/dispatch workspace 1"));
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,121 @@ using namespace Hyprutils::Memory;
|
||||||
#define UP CUniquePointer
|
#define UP CUniquePointer
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
|
|
||||||
|
// Uncomment once test vm can run hyprland-dialog
|
||||||
|
// static void testAnrDialogs() {
|
||||||
|
// NLog::log("{}Testing ANR dialogs", Colors::YELLOW);
|
||||||
|
//
|
||||||
|
// OK(getFromSocket("/keyword misc:enable_anr_dialog true"));
|
||||||
|
// OK(getFromSocket("/keyword misc:anr_missed_pings 1"));
|
||||||
|
//
|
||||||
|
// NLog::log("{}ANR dialog: regular workspaces", Colors::YELLOW);
|
||||||
|
// {
|
||||||
|
// OK(getFromSocket("/dispatch workspace 2"));
|
||||||
|
//
|
||||||
|
// auto kitty = Tests::spawnKitty("bad_kitty");
|
||||||
|
//
|
||||||
|
// if (!kitty) {
|
||||||
|
// ret = 1;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// auto str = getFromSocket("/activewindow");
|
||||||
|
// EXPECT_CONTAINS(str, "workspace: 2");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
//
|
||||||
|
// ::kill(kitty->pid(), SIGSTOP);
|
||||||
|
// Tests::waitUntilWindowsN(2);
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// auto str = getFromSocket("/activeworkspace");
|
||||||
|
// EXPECT_CONTAINS(str, "windows: 0");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog"))
|
||||||
|
// auto str = getFromSocket("/activewindow");
|
||||||
|
// EXPECT_CONTAINS(str, "workspace: 2");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Tests::killAllWindows();
|
||||||
|
//
|
||||||
|
// NLog::log("{}ANR dialog: named workspaces", Colors::YELLOW);
|
||||||
|
// {
|
||||||
|
// OK(getFromSocket("/dispatch workspace name:yummy"));
|
||||||
|
//
|
||||||
|
// auto kitty = Tests::spawnKitty("bad_kitty");
|
||||||
|
//
|
||||||
|
// if (!kitty) {
|
||||||
|
// ret = 1;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// auto str = getFromSocket("/activewindow");
|
||||||
|
// EXPECT_CONTAINS(str, "yummy");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
//
|
||||||
|
// ::kill(kitty->pid(), SIGSTOP);
|
||||||
|
// Tests::waitUntilWindowsN(2);
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// auto str = getFromSocket("/activeworkspace");
|
||||||
|
// EXPECT_CONTAINS(str, "windows: 0");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog"))
|
||||||
|
// auto str = getFromSocket("/activewindow");
|
||||||
|
// EXPECT_CONTAINS(str, "yummy");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Tests::killAllWindows();
|
||||||
|
//
|
||||||
|
// NLog::log("{}ANR dialog: special workspaces", Colors::YELLOW);
|
||||||
|
// {
|
||||||
|
// OK(getFromSocket("/dispatch workspace special:apple"));
|
||||||
|
//
|
||||||
|
// auto kitty = Tests::spawnKitty("bad_kitty");
|
||||||
|
//
|
||||||
|
// if (!kitty) {
|
||||||
|
// ret = 1;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// auto str = getFromSocket("/activewindow");
|
||||||
|
// EXPECT_CONTAINS(str, "special:apple");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// OK(getFromSocket("/dispatch togglespecialworkspace apple"));
|
||||||
|
// OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
//
|
||||||
|
// ::kill(kitty->pid(), SIGSTOP);
|
||||||
|
// Tests::waitUntilWindowsN(2);
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// auto str = getFromSocket("/activeworkspace");
|
||||||
|
// EXPECT_CONTAINS(str, "windows: 0");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// OK(getFromSocket("/dispatch focuswindow class:hyprland-dialog"))
|
||||||
|
// auto str = getFromSocket("/activewindow");
|
||||||
|
// EXPECT_CONTAINS(str, "special:apple");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// OK(getFromSocket("/reload"));
|
||||||
|
// Tests::killAllWindows();
|
||||||
|
// }
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing config: misc:", Colors::GREEN);
|
NLog::log("{}Testing config: misc:", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -53,7 +168,7 @@ static bool test() {
|
||||||
|
|
||||||
NLog::log("{}Testing new_window_takes_over_fullscreen", Colors::YELLOW);
|
NLog::log("{}Testing new_window_takes_over_fullscreen", Colors::YELLOW);
|
||||||
|
|
||||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
||||||
|
|
||||||
Tests::spawnKitty("kitty_A");
|
Tests::spawnKitty("kitty_A");
|
||||||
|
|
||||||
|
|
@ -73,7 +188,16 @@ static bool test() {
|
||||||
EXPECT_CONTAINS(str, "kitty_A");
|
EXPECT_CONTAINS(str, "kitty_A");
|
||||||
}
|
}
|
||||||
|
|
||||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 1"));
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
|
||||||
|
{
|
||||||
|
// should be ignored as per focus_under_fullscreen 0
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||||
|
EXPECT_CONTAINS(str, "kitty_A");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));
|
||||||
|
|
||||||
Tests::spawnKitty("kitty_C");
|
Tests::spawnKitty("kitty_C");
|
||||||
|
|
||||||
|
|
@ -83,7 +207,7 @@ static bool test() {
|
||||||
EXPECT_CONTAINS(str, "kitty_C");
|
EXPECT_CONTAINS(str, "kitty_C");
|
||||||
}
|
}
|
||||||
|
|
||||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 2"));
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));
|
||||||
|
|
||||||
Tests::spawnKitty("kitty_D");
|
Tests::spawnKitty("kitty_D");
|
||||||
|
|
||||||
|
|
@ -93,7 +217,7 @@ static bool test() {
|
||||||
EXPECT_CONTAINS(str, "kitty_D");
|
EXPECT_CONTAINS(str, "kitty_D");
|
||||||
}
|
}
|
||||||
|
|
||||||
OK(getFromSocket("/keyword misc:new_window_takes_over_fullscreen 0"));
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
||||||
|
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
|
@ -138,6 +262,7 @@ static bool test() {
|
||||||
Tests::spawnKitty("kitty_A");
|
Tests::spawnKitty("kitty_A");
|
||||||
Tests::spawnKitty("kitty_B");
|
Tests::spawnKitty("kitty_B");
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
228
hyprtester/src/tests/main/scroll.cpp
Normal file
228
hyprtester/src/tests/main/scroll.cpp
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
#include "../shared.hpp"
|
||||||
|
#include "../../shared.hpp"
|
||||||
|
#include "../../hyprctlCompat.hpp"
|
||||||
|
#include "tests.hpp"
|
||||||
|
|
||||||
|
static int ret = 0;
|
||||||
|
|
||||||
|
static void testFocusCycling() {
|
||||||
|
for (auto const& win : {"a", "b", "c", "d"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: d");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movewindow l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: d");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus u"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testFocusWrapping() {
|
||||||
|
for (auto const& win : {"a", "b", "c", "d"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set wrap_focus to true
|
||||||
|
OK(getFromSocket("/keyword scrolling:wrap_focus true"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: d");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: a");
|
||||||
|
}
|
||||||
|
|
||||||
|
// set wrap_focus to false
|
||||||
|
OK(getFromSocket("/keyword scrolling:wrap_focus false"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: a");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:d"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: d");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSwapcolWrapping() {
|
||||||
|
for (auto const& win : {"a", "b", "c", "d"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set wrap_swapcol to true
|
||||||
|
OK(getFromSocket("/keyword scrolling:wrap_swapcol true"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg swapcol l"));
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
for (auto const& win : {"a", "b", "c", "d"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:d"));
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg swapcol r"));
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
for (auto const& win : {"a", "b", "c", "d"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set wrap_swapcol to false
|
||||||
|
OK(getFromSocket("/keyword scrolling:wrap_swapcol false"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg swapcol l"));
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:d"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg swapcol r"));
|
||||||
|
OK(getFromSocket("/dispatch layoutmsg focus l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test() {
|
||||||
|
NLog::log("{}Testing Scroll layout", Colors::GREEN);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
OK(getFromSocket("/dispatch workspace name:scroll"));
|
||||||
|
OK(getFromSocket("/keyword general:layout scrolling"));
|
||||||
|
|
||||||
|
// test
|
||||||
|
NLog::log("{}Testing focus cycling", Colors::GREEN);
|
||||||
|
testFocusCycling();
|
||||||
|
|
||||||
|
// test
|
||||||
|
NLog::log("{}Testing focus wrap", Colors::GREEN);
|
||||||
|
testFocusWrapping();
|
||||||
|
|
||||||
|
// test
|
||||||
|
NLog::log("{}Testing swapcol wrap", Colors::GREEN);
|
||||||
|
testSwapcolWrapping();
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
NLog::log("Cleaning up", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
return !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_TEST_FN(test);
|
||||||
|
|
@ -21,21 +21,24 @@ static bool testTags() {
|
||||||
|
|
||||||
NLog::log("{}Testing testTag tags", Colors::YELLOW);
|
NLog::log("{}Testing testTag tags", Colors::YELLOW);
|
||||||
|
|
||||||
OK(getFromSocket("/keyword windowrule tag +testTag, class:tagged"));
|
OK(getFromSocket("/keyword windowrule[tag-test-1]:tag +testTag"));
|
||||||
OK(getFromSocket("/keyword windowrule noshadow, tag:negative:testTag"));
|
OK(getFromSocket("/keyword windowrule[tag-test-1]:match:class tagged"));
|
||||||
OK(getFromSocket("/keyword windowrule noborder, tag:testTag"));
|
OK(getFromSocket("/keyword windowrule[tag-test-2]:match:tag negative:testTag"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[tag-test-2]:no_shadow true"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[tag-test-3]:match:tag testTag"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[tag-test-3]:no_dim true"));
|
||||||
|
|
||||||
EXPECT(Tests::windowCount(), 2);
|
EXPECT(Tests::windowCount(), 2);
|
||||||
OK(getFromSocket("/dispatch focuswindow class:tagged"));
|
OK(getFromSocket("/dispatch focuswindow class:tagged"));
|
||||||
NLog::log("{}Testing tagged window for noborder & noshadow", Colors::YELLOW);
|
NLog::log("{}Testing tagged window for no_dim 0 & no_shadow", Colors::YELLOW);
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag");
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag");
|
||||||
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noborder"), "true");
|
EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "true");
|
||||||
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "false");
|
EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "false");
|
||||||
NLog::log("{}Testing untagged window for noborder & noshadow", Colors::YELLOW);
|
NLog::log("{}Testing untagged window for no_dim & no_shadow", Colors::YELLOW);
|
||||||
OK(getFromSocket("/dispatch focuswindow class:untagged"));
|
OK(getFromSocket("/dispatch focuswindow class:untagged"));
|
||||||
EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag");
|
EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag");
|
||||||
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noborder"), "false");
|
EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "true");
|
||||||
EXPECT_CONTAINS(getFromSocket("/getprop activewindow noshadow"), "true");
|
EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "false");
|
||||||
|
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
|
#include <unistd.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <thread>
|
||||||
#include <hyprutils/os/Process.hpp>
|
#include <hyprutils/os/Process.hpp>
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
|
#include <hyprutils/string/VarList2.hpp>
|
||||||
|
|
||||||
#include "../../shared.hpp"
|
#include "../../shared.hpp"
|
||||||
#include "../../hyprctlCompat.hpp"
|
#include "../../hyprctlCompat.hpp"
|
||||||
|
|
@ -11,15 +16,37 @@
|
||||||
|
|
||||||
static int ret = 0;
|
static int ret = 0;
|
||||||
|
|
||||||
static bool spawnKitty(const std::string& class_) {
|
static bool spawnKitty(const std::string& class_, const std::vector<std::string>& args = {}) {
|
||||||
NLog::log("{}Spawning {}", Colors::YELLOW, class_);
|
NLog::log("{}Spawning {}", Colors::YELLOW, class_);
|
||||||
if (!Tests::spawnKitty(class_)) {
|
if (!Tests::spawnKitty(class_, args)) {
|
||||||
NLog::log("{}Error: {} did not spawn", Colors::RED, class_);
|
NLog::log("{}Error: {} did not spawn", Colors::RED, class_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Spawns a kitty and creates a file and returns its name. The removal of the file triggers
|
||||||
|
/// activation of the spawned kitty window.
|
||||||
|
///
|
||||||
|
/// On failure, returns an empty string, possibly leaving a temporary file.
|
||||||
|
static std::string spawnKittyActivating(const std::string& class_ = "kitty_activating") {
|
||||||
|
// `XXXXXX` is what `mkstemp` expects to find in the string
|
||||||
|
std::string tmpFilename = (std::filesystem::temp_directory_path() / "XXXXXX").string();
|
||||||
|
int fd = mkstemp(tmpFilename.data());
|
||||||
|
if (fd < 0) {
|
||||||
|
NLog::log("{}Error: could not create tmp file: errno {}", Colors::RED, errno);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
(void)close(fd);
|
||||||
|
bool ok =
|
||||||
|
spawnKitty(class_, {"-o", "allow_remote_control=yes", "--", "/bin/sh", "-c", "while [ -f \"" + tmpFilename + "\" ]; do :; done; kitten @ focus-window; sleep infinity"});
|
||||||
|
if (!ok) {
|
||||||
|
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return tmpFilename;
|
||||||
|
}
|
||||||
|
|
||||||
static std::string getWindowAttribute(const std::string& winInfo, const std::string& attr) {
|
static std::string getWindowAttribute(const std::string& winInfo, const std::string& attr) {
|
||||||
auto pos = winInfo.find(attr);
|
auto pos = winInfo.find(attr);
|
||||||
if (pos == std::string::npos) {
|
if (pos == std::string::npos) {
|
||||||
|
|
@ -66,9 +93,9 @@ static void testSwapWindow() {
|
||||||
{
|
{
|
||||||
getFromSocket("/dispatch focuswindow class:kitty_A");
|
getFromSocket("/dispatch focuswindow class:kitty_A");
|
||||||
auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:");
|
auto pos = getWindowAttribute(getFromSocket("/activewindow"), "at:");
|
||||||
NLog::log("{}Testing kitty_A {}, swapwindow with direction 'l'", Colors::YELLOW, pos);
|
NLog::log("{}Testing kitty_A {}, swapwindow with direction 'r'", Colors::YELLOW, pos);
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch swapwindow l"));
|
OK(getFromSocket("/dispatch swapwindow r"));
|
||||||
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos));
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", pos));
|
||||||
|
|
@ -131,6 +158,506 @@ static void testSwapWindow() {
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testGroupRules() {
|
||||||
|
NLog::log("{}Testing group window rules", Colors::YELLOW);
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword general:border_size 8"));
|
||||||
|
OK(getFromSocket("/keyword workspace w[tv1], bordersize:0"));
|
||||||
|
OK(getFromSocket("/keyword workspace f[1], bordersize:0"));
|
||||||
|
OK(getFromSocket("/keyword windowrule match:workspace w[tv1], border_size 0"));
|
||||||
|
OK(getFromSocket("/keyword windowrule match:workspace f[1], border_size 0"));
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("kitty_A")) {
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("kitty_B")) {
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "8");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
OK(getFromSocket("/dispatch togglegroup"));
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
OK(getFromSocket("/dispatch moveintogroup l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch changegroupactive f"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("kitty_C")) {
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch moveoutofgroup r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "8");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isActiveWindow(const std::string& class_, char fullscreen = '0', bool log = true) {
|
||||||
|
std::string activeWin = getFromSocket("/activewindow");
|
||||||
|
auto winClass = getWindowAttribute(activeWin, "class:");
|
||||||
|
auto winFullscreen = getWindowAttribute(activeWin, "fullscreen:").back();
|
||||||
|
if (winClass.substr(strlen("class: ")) == class_ && winFullscreen == fullscreen)
|
||||||
|
return true;
|
||||||
|
else {
|
||||||
|
if (log)
|
||||||
|
NLog::log("{}Wrong active window: expected class {} fullscreen '{}', found class {}, fullscreen '{}'", Colors::RED, class_, fullscreen, winClass, winFullscreen);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool waitForActiveWindow(const std::string& class_, char fullscreen = '0', bool logLastCheck = true, int maxTries = 50) {
|
||||||
|
int cnt = 0;
|
||||||
|
while (!isActiveWindow(class_, fullscreen, false)) {
|
||||||
|
++cnt;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
if (cnt > maxTries) {
|
||||||
|
return isActiveWindow(class_, fullscreen, logLastCheck);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests behavior of a window being focused when on that window's workspace
|
||||||
|
/// another fullscreen window exists.
|
||||||
|
static bool testWindowFocusOnFullscreenConflict() {
|
||||||
|
if (!spawnKitty("kitty_A"))
|
||||||
|
return false;
|
||||||
|
if (!spawnKitty("kitty_B"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword misc:focus_on_activate true"));
|
||||||
|
|
||||||
|
// Unfullscreen on conflict
|
||||||
|
{
|
||||||
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 2"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
|
||||||
|
// Dispatch-focus the same window
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
|
||||||
|
// Dispatch-focus a different window
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
EXPECT(isActiveWindow("kitty_B", '0'), true);
|
||||||
|
|
||||||
|
// Make a window that will request focus
|
||||||
|
const std::string removeToActivate = spawnKittyActivating();
|
||||||
|
if (removeToActivate.empty())
|
||||||
|
return false;
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
std::filesystem::remove(removeToActivate);
|
||||||
|
EXPECT(waitForActiveWindow("kitty_activating", '0'), true);
|
||||||
|
OK(getFromSocket("/dispatch forcekillactive"));
|
||||||
|
Tests::waitUntilWindowsN(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take over on conflict
|
||||||
|
{
|
||||||
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 1"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
|
||||||
|
// Dispatch-focus the same window
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
|
||||||
|
// Dispatch-focus a different window
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
EXPECT(isActiveWindow("kitty_B", '2'), true);
|
||||||
|
OK(getFromSocket("/dispatch fullscreenstate 0 0"));
|
||||||
|
|
||||||
|
// Make a window that will request focus
|
||||||
|
const std::string removeToActivate = spawnKittyActivating();
|
||||||
|
if (removeToActivate.empty())
|
||||||
|
return false;
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
std::filesystem::remove(removeToActivate);
|
||||||
|
EXPECT(waitForActiveWindow("kitty_activating", '2'), true);
|
||||||
|
OK(getFromSocket("/dispatch forcekillactive"));
|
||||||
|
Tests::waitUntilWindowsN(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the old focus on conflict
|
||||||
|
{
|
||||||
|
OK(getFromSocket("/keyword misc:on_focus_under_fullscreen 0"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
|
||||||
|
// Dispatch-focus the same window
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
|
||||||
|
// Make a window that will request focus - the setting is treated normally
|
||||||
|
const std::string removeToActivate = spawnKittyActivating();
|
||||||
|
if (removeToActivate.empty())
|
||||||
|
return false;
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
EXPECT(isActiveWindow("kitty_A", '2'), true);
|
||||||
|
std::filesystem::remove(removeToActivate);
|
||||||
|
EXPECT(waitForActiveWindow("kitty_A", '2'), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testMaximizeSize() {
|
||||||
|
NLog::log("{}Testing maximize size", Colors::GREEN);
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("kitty_A"), true);
|
||||||
|
|
||||||
|
// check kitty properties. Maximizing shouldnt change its size
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT(str.contains("at: 22,22"), true);
|
||||||
|
EXPECT(str.contains("size: 1876,1036"), true);
|
||||||
|
EXPECT(str.contains("fullscreen: 0"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 1"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT(str.contains("at: 22,22"), true);
|
||||||
|
EXPECT(str.contains("size: 1876,1036"), true);
|
||||||
|
EXPECT(str.contains("fullscreen: 1"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testFloatingFocusOnFullscreen() {
|
||||||
|
NLog::log("{}Testing floating focus on fullscreen", Colors::GREEN);
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("kitty_A"), true);
|
||||||
|
OK(getFromSocket("/dispatch togglefloating"));
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("kitty_B"), true);
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 1"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch cyclenext"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:floating_focus_on_fullscreen"));
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testGroupFallbackFocus() {
|
||||||
|
NLog::log("{}Testing group fallback focus", Colors::GREEN);
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("kitty_A"), true);
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch togglegroup"));
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("kitty_B"), true);
|
||||||
|
EXPECT(spawnKitty("kitty_C"), true);
|
||||||
|
EXPECT(spawnKitty("kitty_D"), true);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("class: kitty_D"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_B"));
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_D"));
|
||||||
|
OK(getFromSocket("/dispatch killactive"));
|
||||||
|
|
||||||
|
Tests::waitUntilWindowsN(3);
|
||||||
|
|
||||||
|
// Focus must return to the last focus, in this case B.
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("class: kitty_B"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testBringActiveToTopMouseMovement() {
|
||||||
|
NLog::log("{}Testing bringactivetotop mouse movement", Colors::GREEN);
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
OK(getFromSocket("/keyword input:follow_mouse 2"));
|
||||||
|
OK(getFromSocket("/keyword input:float_switch_override_focus 0"));
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("a"), true);
|
||||||
|
OK(getFromSocket("/dispatch setfloating"));
|
||||||
|
OK(getFromSocket("/dispatch movewindowpixel exact 500 300,activewindow"));
|
||||||
|
OK(getFromSocket("/dispatch resizewindowpixel exact 400 400,activewindow"));
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("b"), true);
|
||||||
|
OK(getFromSocket("/dispatch setfloating"));
|
||||||
|
OK(getFromSocket("/dispatch movewindowpixel exact 500 300,activewindow"));
|
||||||
|
OK(getFromSocket("/dispatch resizewindowpixel exact 400 400,activewindow"));
|
||||||
|
|
||||||
|
auto getTopWindow = []() -> std::string {
|
||||||
|
auto clients = getFromSocket("/clients");
|
||||||
|
return (clients.rfind("class: a") > clients.rfind("class: b")) ? "a" : "b";
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT(getTopWindow(), std::string("b"));
|
||||||
|
OK(getFromSocket("/dispatch movecursor 700 500"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: a");
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch bringactivetotop"));
|
||||||
|
EXPECT(getTopWindow(), std::string("a"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:click 272,1"));
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:click 272,0"));
|
||||||
|
|
||||||
|
EXPECT(getTopWindow(), std::string("a"));
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testInitialFloatSize() {
|
||||||
|
NLog::log("{}Testing initial float size", Colors::GREEN);
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class kitty, float yes"));
|
||||||
|
OK(getFromSocket("/keyword input:float_switch_override_focus 0"));
|
||||||
|
|
||||||
|
EXPECT(spawnKitty("kitty"), true);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Kitty by default opens as 640x400, if this changes this test will break
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT(str.contains("size: 640,400"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch exec [float yes]kitty"));
|
||||||
|
|
||||||
|
Tests::waitUntilWindowsN(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
// Kitty by default opens as 640x400, if this changes this test will break
|
||||||
|
auto str = getFromSocket("/clients");
|
||||||
|
EXPECT(str.contains("size: 640,400"), true);
|
||||||
|
EXPECT(str.contains("floating: 1"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests that the `focus_on_activate` effect of window rules always overrides
|
||||||
|
/// the `misc:focus_on_activate` variable.
|
||||||
|
static bool testWindowRuleFocusOnActivate() {
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
if (!spawnKitty("kitty_default")) {
|
||||||
|
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not focus anyone automatically
|
||||||
|
///////////OK(getFromSocket("/keyword windowrule match:class .*, no_initial_focus true"));
|
||||||
|
|
||||||
|
// `focus_on_activate off` takes over
|
||||||
|
{
|
||||||
|
OK(getFromSocket("/keyword misc:focus_on_activate true"));
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class kitty_antifocus, focus_on_activate off"));
|
||||||
|
|
||||||
|
const std::string removeToActivate = spawnKittyActivating("kitty_antifocus");
|
||||||
|
if (removeToActivate.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPECT(waitForActiveWindow("kitty_antifocus"), true);
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_default"));
|
||||||
|
EXPECT(isActiveWindow("kitty_default"), true);
|
||||||
|
|
||||||
|
std::filesystem::remove(removeToActivate);
|
||||||
|
// The focus should NOT transition, since the window rule explicitly forbids that
|
||||||
|
EXPECT(waitForActiveWindow("kitty_antifocus", '0', false), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `focus_on_activate on` takes over
|
||||||
|
{
|
||||||
|
OK(getFromSocket("/keyword misc:focus_on_activate false"));
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class kitty_superfocus, focus_on_activate on"));
|
||||||
|
|
||||||
|
const std::string removeToActivate = spawnKittyActivating("kitty_superfocus");
|
||||||
|
if (removeToActivate.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
EXPECT(waitForActiveWindow("kitty_superfocus"), true);
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_default"));
|
||||||
|
EXPECT(isActiveWindow("kitty_default"), true);
|
||||||
|
|
||||||
|
std::filesystem::remove(removeToActivate);
|
||||||
|
// Now that we requested activation, the focus SHOULD transition to kitty_superfocus, according to the window rule
|
||||||
|
EXPECT(waitForActiveWindow("kitty_superfocus"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests if a pinned window contains the valid workspace after change
|
||||||
|
static bool testPinnedWorkspacesValid() {
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
getFromSocket("/dispatch workspace 1337");
|
||||||
|
|
||||||
|
if (!spawnKitty("kitty")) {
|
||||||
|
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch setfloating class:kitty"));
|
||||||
|
OK(getFromSocket("/dispatch pin class:kitty"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("workspace: 1337"), true);
|
||||||
|
EXPECT(str.contains("pinned: 1"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromSocket("/dispatch workspace 1338");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("workspace: 1338"), true);
|
||||||
|
EXPECT(str.contains("pinned: 1"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch settiled class:kitty"))
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("workspace: 1338"), true);
|
||||||
|
EXPECT(str.contains("pinned: 0"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool testWindowRuleWorkspaceEmpty() {
|
||||||
|
NLog::log("{}Testing windowrule workspace empty", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class kitty_A, workspace empty"));
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class kitty_B, workspace emptyn"));
|
||||||
|
|
||||||
|
getFromSocket("/dispatch workspace 3");
|
||||||
|
|
||||||
|
if (!spawnKitty("kitty")) {
|
||||||
|
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("workspace: 3"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spawnKitty("kitty_A")) {
|
||||||
|
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("workspace: 1"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromSocket("/dispatch workspace 3");
|
||||||
|
if (!spawnKitty("kitty_B")) {
|
||||||
|
NLog::log("{}Error: failed to spawn kitty", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT(str.contains("workspace: 4"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing windows", Colors::GREEN);
|
NLog::log("{}Testing windows", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -161,13 +688,13 @@ static bool test() {
|
||||||
const int MONITOR_H = 1080;
|
const int MONITOR_H = 1080;
|
||||||
|
|
||||||
const float totalAvailableHeight = MONITOR_H - (GAPSOUT * 2);
|
const float totalAvailableHeight = MONITOR_H - (GAPSOUT * 2);
|
||||||
const int HEIGHT = std::round(totalAvailableHeight) - BORDERS;
|
const int HEIGHT = std::floor(totalAvailableHeight) - BORDERS;
|
||||||
const float availableWidthForSplit = MONITOR_W - (GAPSOUT * 2) - GAPSIN;
|
const float availableWidthForSplit = MONITOR_W - (GAPSOUT * 2) - GAPSIN;
|
||||||
|
|
||||||
auto calculateFinalWidth = [&](double boxWidth, bool isLeftWindow) {
|
auto calculateFinalWidth = [&](double boxWidth, bool isLeftWindow) {
|
||||||
double gapLeft = isLeftWindow ? GAPSOUT : GAPSIN;
|
double gapLeft = isLeftWindow ? GAPSOUT : GAPSIN;
|
||||||
double gapRight = isLeftWindow ? GAPSIN : GAPSOUT;
|
double gapRight = isLeftWindow ? GAPSIN : GAPSOUT;
|
||||||
return std::round(boxWidth - gapLeft - gapRight - BORDERS);
|
return std::floor(boxWidth - gapLeft - gapRight - BORDERS);
|
||||||
};
|
};
|
||||||
|
|
||||||
double geomBoxWidthA_R1 = (availableWidthForSplit * INITIAL_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0);
|
double geomBoxWidthA_R1 = (availableWidthForSplit * INITIAL_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0);
|
||||||
|
|
@ -197,12 +724,38 @@ static bool test() {
|
||||||
if (!spawnKitty("kitty_B"))
|
if (!spawnKitty("kitty_B"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
try {
|
||||||
NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH2, HEIGHT);
|
NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH2, HEIGHT);
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH2, HEIGHT));
|
|
||||||
|
{
|
||||||
|
auto data = getFromSocket("/activewindow");
|
||||||
|
data = data.substr(data.find("size:") + 5);
|
||||||
|
data = data.substr(0, data.find('\n'));
|
||||||
|
|
||||||
|
Hyprutils::String::CVarList2 sizes(std::move(data), 0, ',');
|
||||||
|
|
||||||
|
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH2, 2);
|
||||||
|
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2);
|
||||||
|
}
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
|
||||||
NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT);
|
NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT);
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH_A_FINAL, HEIGHT));
|
|
||||||
|
{
|
||||||
|
auto data = getFromSocket("/activewindow");
|
||||||
|
data = data.substr(data.find("size:") + 5);
|
||||||
|
data = data.substr(0, data.find('\n'));
|
||||||
|
|
||||||
|
Hyprutils::String::CVarList2 sizes(std::move(data), 0, ',');
|
||||||
|
|
||||||
|
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH_A_FINAL, 2);
|
||||||
|
EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
NLog::log("{}Exception thrown", Colors::RED);
|
||||||
|
EXPECT(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
OK(getFromSocket("/keyword dwindle:default_split_ratio 1"));
|
OK(getFromSocket("/keyword dwindle:default_split_ratio 1"));
|
||||||
}
|
}
|
||||||
|
|
@ -212,29 +765,20 @@ static bool test() {
|
||||||
getFromSocket("/dispatch exec xeyes");
|
getFromSocket("/dispatch exec xeyes");
|
||||||
|
|
||||||
NLog::log("{}Keep checking if xeyes spawned", Colors::YELLOW);
|
NLog::log("{}Keep checking if xeyes spawned", Colors::YELLOW);
|
||||||
int counter = 0;
|
Tests::waitUntilWindowsN(3);
|
||||||
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);
|
NLog::log("{}Expecting 3 windows", Colors::YELLOW);
|
||||||
EXPECT(Tests::windowCount(), 3);
|
EXPECT(Tests::windowCount(), 3);
|
||||||
|
|
||||||
NLog::log("{}Checking props of xeyes", Colors::YELLOW);
|
NLog::log("{}Checking props of xeyes", Colors::YELLOW);
|
||||||
// check some window props of xeyes, try to tile them
|
// check some window props of xeyes, try to float it
|
||||||
{
|
{
|
||||||
auto str = getFromSocket("/clients");
|
auto str = getFromSocket("/clients");
|
||||||
EXPECT_CONTAINS(str, "floating: 1");
|
EXPECT_NOT_CONTAINS(str, "floating: 1");
|
||||||
getFromSocket("/dispatch settiled class:XEyes");
|
getFromSocket("/dispatch setfloating class:XEyes");
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||||
str = getFromSocket("/clients");
|
str = getFromSocket("/clients");
|
||||||
EXPECT_NOT_CONTAINS(str, "floating: 1");
|
EXPECT_CONTAINS(str, "floating: 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// kill all
|
// kill all
|
||||||
|
|
@ -246,12 +790,45 @@ static bool test() {
|
||||||
|
|
||||||
testSwapWindow();
|
testSwapWindow();
|
||||||
|
|
||||||
|
getFromSocket("/dispatch workspace 1");
|
||||||
|
|
||||||
|
if (!testWindowFocusOnFullscreenConflict()) {
|
||||||
|
ret = 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Testing spawning a floating window over a fullscreen window", Colors::YELLOW);
|
||||||
|
{
|
||||||
|
if (!spawnKitty("kitty_A"))
|
||||||
|
return false;
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0 set"));
|
||||||
|
EXPECT(Tests::windowCount(), 1);
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch exec [float] kitty"));
|
||||||
|
Tests::waitUntilWindowsN(2);
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:^kitty$"));
|
||||||
|
const auto focused1 = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(focused1, "class: kitty\n");
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch killwindow activewindow"));
|
||||||
|
Tests::waitUntilWindowsN(1);
|
||||||
|
|
||||||
|
// The old window should be focused again
|
||||||
|
const auto focused2 = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(focused2, "class: kitty_A\n");
|
||||||
|
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW);
|
NLog::log("{}Testing minsize/maxsize rules for tiled windows", Colors::YELLOW);
|
||||||
{
|
{
|
||||||
// Enable the config for testing, test max/minsize for tiled windows and centering
|
// Enable the config for testing, test max/minsize for tiled windows and centering
|
||||||
OK(getFromSocket("/keyword misc:size_limits_tiled 1"));
|
OK(getFromSocket("/keyword misc:size_limits_tiled 1"));
|
||||||
OK(getFromSocket("/keyword windowrule maxsize 1500 500, class:kitty_maxsize"));
|
OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize"));
|
||||||
OK(getFromSocket("/keyword windowrule minsize 1200 500, class:kitty_maxsize"));
|
OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1500 500"));
|
||||||
|
OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500"));
|
||||||
if (!spawnKitty("kitty_maxsize"))
|
if (!spawnKitty("kitty_maxsize"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -259,10 +836,10 @@ static bool test() {
|
||||||
EXPECT_CONTAINS(dwindle, "size: 1500,500");
|
EXPECT_CONTAINS(dwindle, "size: 1500,500");
|
||||||
EXPECT_CONTAINS(dwindle, "at: 210,290");
|
EXPECT_CONTAINS(dwindle, "at: 210,290");
|
||||||
|
|
||||||
if (!spawnKitty("kitty_maxsize"))
|
// Fuck this test, it's fucking stupid - vax
|
||||||
return false;
|
// if (!spawnKitty("kitty_maxsize"))
|
||||||
|
// return false;
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500");
|
// EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500");
|
||||||
|
|
||||||
Tests::killAllWindows();
|
Tests::killAllWindows();
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
@ -279,8 +856,69 @@ static bool test() {
|
||||||
if (!spawnKitty("kitty_maxsize"))
|
if (!spawnKitty("kitty_maxsize"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// FIXME: I can't be arsed.
|
||||||
OK(getFromSocket("/dispatch focuswindow class:kitty_maxsize"));
|
OK(getFromSocket("/dispatch focuswindow class:kitty_maxsize"));
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500")
|
// EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500")
|
||||||
|
|
||||||
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Testing minsize/maxsize rules", Colors::YELLOW);
|
||||||
|
{
|
||||||
|
// Disable size limits tiled and check if props are working and not getting skipped
|
||||||
|
OK(getFromSocket("/keyword misc:size_limits_tiled 0"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1500 500"));
|
||||||
|
OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500"));
|
||||||
|
if (!spawnKitty("kitty_maxsize"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto res = getFromSocket("/getprop active max_size");
|
||||||
|
EXPECT_CONTAINS(res, "1500");
|
||||||
|
EXPECT_CONTAINS(res, "500");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto res = getFromSocket("/getprop active min_size");
|
||||||
|
EXPECT_CONTAINS(res, "1200");
|
||||||
|
EXPECT_CONTAINS(res, "500");
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Set float
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-max-rule]:match:class kitty_maxsize"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[kitty-max-rule]:max_size 1200 500"));
|
||||||
|
OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:min_size 1200 500"));
|
||||||
|
OK(getFromSocket("r/keyword windowrule[kitty-max-rule]:float yes"));
|
||||||
|
if (!spawnKitty("kitty_maxsize"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto res = getFromSocket("/getprop active max_size");
|
||||||
|
EXPECT_CONTAINS(res, "1200");
|
||||||
|
EXPECT_CONTAINS(res, "500");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto res = getFromSocket("/getprop active min_size");
|
||||||
|
EXPECT_CONTAINS(res, "1200");
|
||||||
|
EXPECT_CONTAINS(res, "500");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto res = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(res, "size: 1200,500");
|
||||||
|
}
|
||||||
|
|
||||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
|
|
@ -297,29 +935,194 @@ static bool test() {
|
||||||
EXPECT_CONTAINS(str, "floating: 1");
|
EXPECT_CONTAINS(str, "floating: 1");
|
||||||
EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE));
|
EXPECT_CONTAINS(str, std::format("size: {},{}", SIZE, SIZE));
|
||||||
EXPECT_NOT_CONTAINS(str, "pinned: 1");
|
EXPECT_NOT_CONTAINS(str, "pinned: 1");
|
||||||
OK(getFromSocket("/keyword windowrule plugin:someplugin:variable, class:wr_kitty"));
|
}
|
||||||
OK(getFromSocket("/keyword windowrule plugin:someplugin:variable 10, class:wr_kitty"));
|
|
||||||
OK(getFromSocket("/keyword windowrule workspace 1, class:wr_kitty"));
|
OK(getFromSocket("/keyword windowrule[wr-kitty-stuff]:opacity 0.5 0.5 override"));
|
||||||
OK(getFromSocket("/keyword windowrule workspace special:magic, class:magic_kitty"));
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active opacity");
|
||||||
|
EXPECT_CONTAINS(str, "0.5");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword windowrule[special-magic-kitty]:match:class magic_kitty"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[special-magic-kitty]:workspace special:magic"));
|
||||||
|
|
||||||
if (!spawnKitty("magic_kitty"))
|
if (!spawnKitty("magic_kitty"))
|
||||||
return false;
|
return false;
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "special:magic");
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "special:magic");
|
||||||
EXPECT_NOT_CONTAINS(str, "workspace: 9");
|
EXPECT_NOT_CONTAINS(str, "workspace: 9");
|
||||||
}
|
}
|
||||||
|
|
||||||
NLog::log("{}Testing faulty rules", Colors::YELLOW);
|
if (auto str = getFromSocket("/monitors"); str.contains("magic)")) {
|
||||||
{
|
OK(getFromSocket("/dispatch togglespecialworkspace magic"));
|
||||||
const auto PARAM = "Invalid parameter";
|
|
||||||
const auto RULE = "Invalid value";
|
|
||||||
const auto NORULE = "no rules provided";
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/keyword windowrule notarule, class:wr_kitty"), RULE)
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/keyword windowrule class:wr_kitty"), NORULE)
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/keyword windowrule float, class:wr_kitty, size"), PARAM)
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/keyword windowrule float, classI:wr_kitty"), PARAM)
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/keyword windowrule workspace:, class:wr_kitty"), NORULE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword windowrule[border-magic-kitty]:match:class border_kitty"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[border-magic-kitty]:border_color rgba(c6ff00ff) rgba(ff0000ee) 45deg"));
|
||||||
|
|
||||||
|
if (!spawnKitty("border_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:border_kitty"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active active_border_color");
|
||||||
|
EXPECT_CONTAINS(str, "ffc6ff00");
|
||||||
|
EXPECT_CONTAINS(str, "eeff0000");
|
||||||
|
EXPECT_CONTAINS(str, "45deg");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
if (!spawnKitty("tag_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "floating: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
// test rules that overlap effects but don't overlap props
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class overlap_kitty, border_size 0"));
|
||||||
|
OK(getFromSocket("/keyword windowrule match:fullscreen false, border_size 10"));
|
||||||
|
|
||||||
|
if (!spawnKitty("overlap_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "10");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
// test persistent_size between floating window launches
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class persistent_size_kitty, persistent_size true, float true"));
|
||||||
|
|
||||||
|
if (!spawnKitty("persistent_size_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch resizeactive exact 600 400"))
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "size: 600,400");
|
||||||
|
EXPECT_CONTAINS(str, "floating: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
if (!spawnKitty("persistent_size_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "size: 600,400");
|
||||||
|
EXPECT_CONTAINS(str, "floating: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword general:border_size 0"));
|
||||||
|
OK(getFromSocket("/keyword windowrule match:float true, border_size 10"));
|
||||||
|
|
||||||
|
if (!spawnKitty("border_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch togglefloating"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "10");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch togglefloating"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/getprop active border_size");
|
||||||
|
EXPECT_CONTAINS(str, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
// test expression rules
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class expr_kitty, float yes, size monitor_w*0.5 monitor_h*0.5, min_size monitor_w*0.25 monitor_h*0.25, "
|
||||||
|
"max_size monitor_w*0.75 monitor_h*0.75, move 20+(monitor_w*0.1) monitor_h*0.5"));
|
||||||
|
|
||||||
|
if (!spawnKitty("expr_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "floating: 1");
|
||||||
|
EXPECT_CONTAINS(str, "at: 212,540");
|
||||||
|
EXPECT_CONTAINS(str, "size: 960,540");
|
||||||
|
|
||||||
|
auto min = getFromSocket("/getprop active min_size");
|
||||||
|
EXPECT_CONTAINS(min, "480");
|
||||||
|
EXPECT_CONTAINS(min, "270");
|
||||||
|
|
||||||
|
auto max = getFromSocket("/getprop active max_size");
|
||||||
|
EXPECT_CONTAINS(max, "1440");
|
||||||
|
EXPECT_CONTAINS(max, "810");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:add_window_rule"));
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect"));
|
||||||
|
|
||||||
|
if (!spawnKitty("plugin_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:check_window_rule"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:add_window_rule"));
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty"));
|
||||||
|
OK(getFromSocket("/keyword windowrule[test-plugin-rule]:plugin_rule effect"));
|
||||||
|
|
||||||
|
if (!spawnKitty("plugin_kitty"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch plugin:test:check_window_rule"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
testGroupRules();
|
||||||
|
testMaximizeSize();
|
||||||
|
testFloatingFocusOnFullscreen();
|
||||||
|
testBringActiveToTopMouseMovement();
|
||||||
|
testGroupFallbackFocus();
|
||||||
|
testInitialFloatSize();
|
||||||
|
testWindowRuleFocusOnActivate();
|
||||||
|
testPinnedWorkspacesValid();
|
||||||
|
testWindowRuleWorkspaceEmpty();
|
||||||
|
|
||||||
NLog::log("{}Reloading config", Colors::YELLOW);
|
NLog::log("{}Reloading config", Colors::YELLOW);
|
||||||
OK(getFromSocket("/reload"));
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,379 @@ using namespace Hyprutils::Utils;
|
||||||
#define UP CUniquePointer
|
#define UP CUniquePointer
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
|
|
||||||
|
static bool testSpecialWorkspaceFullscreen() {
|
||||||
|
NLog::log("{}Testing special workspace fullscreen detection", Colors::YELLOW);
|
||||||
|
|
||||||
|
CScopeGuard guard = {[&]() {
|
||||||
|
NLog::log("{}Cleaning up special workspace fullscreen test", Colors::YELLOW);
|
||||||
|
// Close special workspace if open
|
||||||
|
auto monitors = getFromSocket("/monitors");
|
||||||
|
if (monitors.contains("(special:test_fs_special)") && !monitors.contains("special workspace: 0 ()"))
|
||||||
|
getFromSocket("/dispatch togglespecialworkspace test_fs_special");
|
||||||
|
Tests::killAllWindows();
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
}};
|
||||||
|
|
||||||
|
getFromSocket("/dispatch workspace 1");
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
NLog::log("{}Test 1: Fullscreen detection on special workspace", Colors::YELLOW);
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace special:test_fs_special"));
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("kitty_special"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: kitty_special");
|
||||||
|
EXPECT_CONTAINS(str, "(special:test_fs_special)");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/monitors");
|
||||||
|
EXPECT_CONTAINS(str, "(special:test_fs_special)");
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Test 2: Special workspace fullscreen precedence", Colors::YELLOW);
|
||||||
|
|
||||||
|
// Close special workspace before spawning on regular workspace
|
||||||
|
OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special"));
|
||||||
|
getFromSocket("/dispatch workspace 1");
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("kitty_regular"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch fullscreen 0"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: kitty_regular");
|
||||||
|
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special"));
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_special"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: kitty_special");
|
||||||
|
}
|
||||||
|
|
||||||
|
NLog::log("{}Test 3: Toggle special workspace hides it", Colors::YELLOW);
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch togglespecialworkspace test_fs_special"));
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:kitty_regular"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: kitty_regular");
|
||||||
|
EXPECT_CONTAINS(str, "fullscreen: 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/monitors");
|
||||||
|
EXPECT_CONTAINS(str, "special workspace: 0 ()");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool testAsymmetricGaps() {
|
||||||
|
NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW);
|
||||||
|
{
|
||||||
|
|
||||||
|
CScopeGuard guard = {[&]() {
|
||||||
|
NLog::log("{}Cleaning up asymmetric gap test", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
}};
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace name:gap_split_test"));
|
||||||
|
OK(getFromSocket("r/keyword general:gaps_in 0"));
|
||||||
|
OK(getFromSocket("r/keyword general:border_size 0"));
|
||||||
|
OK(getFromSocket("r/keyword dwindle:split_width_multiplier 1.0"));
|
||||||
|
OK(getFromSocket("r/keyword workspace name:gap_split_test,gapsout:0 1000 0 0"));
|
||||||
|
|
||||||
|
NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("r/keyword dwindle:force_split 0"));
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
NLog::log("{}Testing force_split = 1", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("r/keyword dwindle:force_split 1"));
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
|
||||||
|
|
||||||
|
NLog::log("{}Expecting horizontal split (C left of B)", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("gaps_kitty_C"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0");
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
||||||
|
NLog::log("{}Testing force_split = 2", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("r/keyword dwindle:force_split 2"));
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
|
||||||
|
|
||||||
|
NLog::log("{}Expecting horizontal split (C right of A)", Colors::YELLOW);
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
||||||
|
|
||||||
|
if (!Tests::spawnKitty("gaps_kitty_C"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C"));
|
||||||
|
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// kill all
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testMultimonBAF() {
|
||||||
|
NLog::log("{}Testing multimon back and forth", Colors::YELLOW);
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword binds:workspace_back_and_forth 1"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
|
||||||
|
Tests::spawnKitty();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 2"));
|
||||||
|
|
||||||
|
Tests::spawnKitty();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focusmonitor HEADLESS-3"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 3"));
|
||||||
|
|
||||||
|
Tests::spawnKitty();
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 3"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 2 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 4"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 4"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 2 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 2"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 4 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 3"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 3"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 4 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 2"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 3"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 1"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 3 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testMultimonFocus() {
|
||||||
|
NLog::log("{}Testing multimon focus and move", Colors::YELLOW);
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword input:follow_mouse 0"));
|
||||||
|
OK(getFromSocket("/dispatch focusmonitor HEADLESS-3"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 8"));
|
||||||
|
OK(getFromSocket("/dispatch focusmonitor HEADLESS-2"));
|
||||||
|
OK(getFromSocket("/dispatch workspace 7"));
|
||||||
|
|
||||||
|
for (auto const& win : {"a", "b"}) {
|
||||||
|
if (!Tests::spawnKitty(win)) {
|
||||||
|
NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win);
|
||||||
|
++TESTS_FAILED;
|
||||||
|
ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch focuswindow class:a"));
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 7 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tests::spawnKitty("c");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 7 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movewindow r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus r"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: c");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movefocus l"));
|
||||||
|
|
||||||
|
OK(getFromSocket("/keyword general:no_focus_fallback true"));
|
||||||
|
OK(getFromSocket("/keyword binds:window_direction_monitor_fallback false"));
|
||||||
|
|
||||||
|
EXPECT_NOT(getFromSocket("/dispatch movefocus l"), "ok");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch movewindow l"));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activewindow");
|
||||||
|
EXPECT_CONTAINS(str, "class: b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto str = getFromSocket("/activeworkspace");
|
||||||
|
EXPECT_CONTAINS(str, "workspace ID 8 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testDynamicWsEffects() {
|
||||||
|
// test dynamic workspace effects, they shouldn't lag
|
||||||
|
|
||||||
|
OK(getFromSocket("/dispatch workspace 69"));
|
||||||
|
|
||||||
|
Tests::spawnKitty("bitch");
|
||||||
|
|
||||||
|
OK(getFromSocket("r/keyword workspace 69,bordersize:20"));
|
||||||
|
OK(getFromSocket("r/keyword workspace 69,rounding:false"));
|
||||||
|
|
||||||
|
EXPECT(getFromSocket("/getprop class:bitch border_size"), "20");
|
||||||
|
EXPECT(getFromSocket("/getprop class:bitch rounding"), "0");
|
||||||
|
|
||||||
|
OK(getFromSocket("/reload"));
|
||||||
|
|
||||||
|
Tests::killAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
static bool test() {
|
static bool test() {
|
||||||
NLog::log("{}Testing workspaces", Colors::GREEN);
|
NLog::log("{}Testing workspaces", Colors::GREEN);
|
||||||
|
|
||||||
|
|
@ -27,7 +400,7 @@ static bool test() {
|
||||||
|
|
||||||
// test on workspace "window"
|
// test on workspace "window"
|
||||||
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
NLog::log("{}Switching to workspace 1", Colors::YELLOW);
|
||||||
OK(getFromSocket("/dispatch workspace 1"));
|
getFromSocket("/dispatch workspace 1");
|
||||||
|
|
||||||
NLog::log("{}Checking persistent no-mon", Colors::YELLOW);
|
NLog::log("{}Checking persistent no-mon", Colors::YELLOW);
|
||||||
OK(getFromSocket("r/keyword workspace 966,persistent:1"));
|
OK(getFromSocket("r/keyword workspace 966,persistent:1"));
|
||||||
|
|
@ -354,101 +727,19 @@ static bool test() {
|
||||||
EXPECT_CONTAINS(str, "class: kitty_B");
|
EXPECT_CONTAINS(str, "class: kitty_B");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// kill all
|
||||||
|
NLog::log("{}Killing all windows", Colors::YELLOW);
|
||||||
|
Tests::killAllWindows();
|
||||||
|
|
||||||
|
testMultimonBAF();
|
||||||
|
testMultimonFocus();
|
||||||
|
|
||||||
// destroy the headless output
|
// destroy the headless output
|
||||||
OK(getFromSocket("/output remove HEADLESS-3"));
|
OK(getFromSocket("/output remove HEADLESS-3"));
|
||||||
|
|
||||||
// kill all
|
testSpecialWorkspaceFullscreen();
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
testAsymmetricGaps();
|
||||||
Tests::killAllWindows();
|
testDynamicWsEffects();
|
||||||
|
|
||||||
NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW);
|
|
||||||
{
|
|
||||||
|
|
||||||
CScopeGuard guard = {[&]() {
|
|
||||||
NLog::log("{}Cleaning up asymmetric gap test", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
OK(getFromSocket("/reload"));
|
|
||||||
}};
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch workspace name:gap_split_test"));
|
|
||||||
OK(getFromSocket("r/keyword general:gaps_in 0"));
|
|
||||||
OK(getFromSocket("r/keyword general:border_size 0"));
|
|
||||||
OK(getFromSocket("r/keyword dwindle:split_width_multiplier 1.0"));
|
|
||||||
OK(getFromSocket("r/keyword workspace name:gap_split_test,gapsout:0 1000 0 0"));
|
|
||||||
|
|
||||||
NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("r/keyword dwindle:force_split 0"));
|
|
||||||
|
|
||||||
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
|
|
||||||
|
|
||||||
Tests::killAllWindows();
|
|
||||||
EXPECT(Tests::windowCount(), 0);
|
|
||||||
|
|
||||||
NLog::log("{}Testing force_split = 1", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("r/keyword dwindle:force_split 1"));
|
|
||||||
|
|
||||||
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
|
|
||||||
|
|
||||||
NLog::log("{}Expecting horizontal split (C left of B)", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
|
||||||
|
|
||||||
if (!Tests::spawnKitty("gaps_kitty_C")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0");
|
|
||||||
|
|
||||||
Tests::killAllWindows();
|
|
||||||
EXPECT(Tests::windowCount(), 0);
|
|
||||||
|
|
||||||
NLog::log("{}Testing force_split = 2", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("r/keyword dwindle:force_split 2"));
|
|
||||||
|
|
||||||
if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
|
|
||||||
|
|
||||||
NLog::log("{}Expecting horizontal split (C right of A)", Colors::YELLOW);
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
|
||||||
|
|
||||||
if (!Tests::spawnKitty("gaps_kitty_C")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
|
|
||||||
OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C"));
|
|
||||||
EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0");
|
|
||||||
}
|
|
||||||
|
|
||||||
// kill all
|
|
||||||
NLog::log("{}Killing all windows", Colors::YELLOW);
|
|
||||||
Tests::killAllWindows();
|
|
||||||
|
|
||||||
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
|
||||||
EXPECT(Tests::windowCount(), 0);
|
EXPECT(Tests::windowCount(), 0);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <print>
|
#include <print>
|
||||||
|
#include <fstream>
|
||||||
#include "../shared.hpp"
|
#include "../shared.hpp"
|
||||||
#include "../hyprctlCompat.hpp"
|
#include "../hyprctlCompat.hpp"
|
||||||
|
|
||||||
|
|
@ -39,6 +40,38 @@ CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_, const std:
|
||||||
return kitty;
|
return kitty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CUniquePointer<CProcess> Tests::spawnLayerKitty(const std::string& namespace_, const std::vector<std::string> args) {
|
||||||
|
std::vector<std::string> programArgs = args;
|
||||||
|
if (!namespace_.empty()) {
|
||||||
|
programArgs.insert(programArgs.begin(), "--class");
|
||||||
|
programArgs.insert(programArgs.begin() + 1, namespace_);
|
||||||
|
}
|
||||||
|
|
||||||
|
programArgs.insert(programArgs.begin(), "+kitten");
|
||||||
|
programArgs.insert(programArgs.begin() + 1, "panel");
|
||||||
|
|
||||||
|
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("kitty", programArgs);
|
||||||
|
kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
||||||
|
kitty->runAsync();
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
|
||||||
|
// wait while the layer spawns
|
||||||
|
int counter = 0;
|
||||||
|
while (processAlive(kitty->pid()) && countOccurrences(getFromSocket("/layers"), std::format("pid: {}", kitty->pid())) == 0) {
|
||||||
|
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) {
|
bool Tests::processAlive(pid_t pid) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
int ret = kill(pid, 0);
|
int ret = kill(pid, 0);
|
||||||
|
|
@ -96,6 +129,38 @@ void Tests::waitUntilWindowsN(int n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Tests::layerCount() {
|
||||||
|
return countOccurrences(getFromSocket("/layers"), "namespace: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tests::killAllLayers() {
|
||||||
|
auto str = getFromSocket("/layers");
|
||||||
|
auto pos = str.find("pid: ");
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
auto pid = stoi(str.substr(pos + 5, str.find('\n', pos)));
|
||||||
|
kill(pid, 15);
|
||||||
|
|
||||||
|
// we need to wait for a bit because for some reason otherwise we'll end up
|
||||||
|
// with layers with pid -1 if they are all removed at the same time
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
pos = str.find("pid: ", pos + 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
while (Tests::layerCount() != 0) {
|
||||||
|
counter++;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
if (counter > 50) {
|
||||||
|
std::println("{}Timed out waiting for layers to close", Colors::RED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string Tests::execAndGet(const std::string& cmd) {
|
std::string Tests::execAndGet(const std::string& cmd) {
|
||||||
CProcess proc("/bin/sh", {"-c", cmd});
|
CProcess proc("/bin/sh", {"-c", cmd});
|
||||||
|
|
||||||
|
|
@ -105,3 +170,14 @@ std::string Tests::execAndGet(const std::string& cmd) {
|
||||||
|
|
||||||
return proc.stdOut();
|
return proc.stdOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Tests::writeFile(const std::string& name, const std::string& contents) {
|
||||||
|
std::ofstream of(name, std::ios::trunc);
|
||||||
|
if (!of.good())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
of << contents;
|
||||||
|
of.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,14 @@
|
||||||
//NOLINTNEXTLINE
|
//NOLINTNEXTLINE
|
||||||
namespace Tests {
|
namespace Tests {
|
||||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "", const std::vector<std::string> args = {});
|
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "", const std::vector<std::string> args = {});
|
||||||
|
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnLayerKitty(const std::string& namespace_ = "", const std::vector<std::string> args = {});
|
||||||
bool processAlive(pid_t pid);
|
bool processAlive(pid_t pid);
|
||||||
int windowCount();
|
int windowCount();
|
||||||
int countOccurrences(const std::string& in, const std::string& what);
|
int countOccurrences(const std::string& in, const std::string& what);
|
||||||
bool killAllWindows();
|
bool killAllWindows();
|
||||||
void waitUntilWindowsN(int n);
|
void waitUntilWindowsN(int n);
|
||||||
|
int layerCount();
|
||||||
|
bool killAllLayers();
|
||||||
std::string execAndGet(const std::string& cmd);
|
std::string execAndGet(const std::string& cmd);
|
||||||
|
bool writeFile(const std::string& name, const std::string& contents);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,17 @@ master {
|
||||||
new_status = master
|
new_status = master
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scrolling {
|
||||||
|
fullscreen_on_one_column = true
|
||||||
|
column_width = 0.5
|
||||||
|
focus_fit_method = 1
|
||||||
|
follow_focus = true
|
||||||
|
follow_min_visible = 1
|
||||||
|
explicit_column_widths = 0.25, 0.333, 0.5, 0.667, 0.75, 1.0
|
||||||
|
wrap_focus = true
|
||||||
|
wrap_swapcol = true
|
||||||
|
}
|
||||||
|
|
||||||
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
# https://wiki.hyprland.org/Configuring/Variables/#misc
|
||||||
misc {
|
misc {
|
||||||
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers
|
||||||
|
|
@ -239,7 +250,7 @@ bind = $mainMod, E, exec, $fileManager
|
||||||
bind = $mainMod, V, togglefloating,
|
bind = $mainMod, V, togglefloating,
|
||||||
bind = $mainMod, R, exec, $menu
|
bind = $mainMod, R, exec, $menu
|
||||||
bind = $mainMod, P, pseudo, # dwindle
|
bind = $mainMod, P, pseudo, # dwindle
|
||||||
bind = $mainMod, J, togglesplit, # dwindle
|
bind = $mainMod, J, layoutmsg, togglesplit, # dwindle
|
||||||
|
|
||||||
# Move focus with mainMod + arrow keys
|
# Move focus with mainMod + arrow keys
|
||||||
bind = $mainMod, left, movefocus, l
|
bind = $mainMod, left, movefocus, l
|
||||||
|
|
@ -318,28 +329,70 @@ submap = reset
|
||||||
### WINDOWS AND WORKSPACES ###
|
### WINDOWS AND WORKSPACES ###
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
|
windowrule {
|
||||||
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
# Ignore maximize requests from apps. You'll probably like this.
|
||||||
|
name = suppress-maximize-events
|
||||||
|
match:class = .*
|
||||||
|
|
||||||
# Example windowrule v1
|
suppress_event = maximize
|
||||||
# windowrule = float, ^(kitty)$
|
}
|
||||||
|
|
||||||
# Example windowrule v2
|
windowrule {
|
||||||
# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
|
# Fix some dragging issues with XWayland
|
||||||
|
name = fix-xwayland-drags
|
||||||
|
match:class = ^$
|
||||||
|
match:title = ^$
|
||||||
|
match:xwayland = true
|
||||||
|
match:float = true
|
||||||
|
match:fullscreen = false
|
||||||
|
match:pin = false
|
||||||
|
|
||||||
# Ignore maximize requests from apps. You'll probably like this.
|
no_focus = true
|
||||||
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] w[tv1], gapsout:0, gapsin:0
|
||||||
workspace = n[s:window] f[1], gapsout:0, gapsin:0
|
workspace = n[s:window] f[1], gapsout:0, gapsin:0
|
||||||
windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] w[tv1]
|
|
||||||
windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] w[tv1]
|
windowrule {
|
||||||
windowrulev2 = bordersize 0, floating:0, onworkspace:n[s:window] f[1]
|
name = smart-gaps-1
|
||||||
windowrulev2 = rounding 0, floating:0, onworkspace:n[s:window] f[1]
|
match:float = false
|
||||||
|
match:workspace = n[s:window] w[tv1]
|
||||||
|
|
||||||
|
border_size = 0
|
||||||
|
rounding = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = smart-gaps-2
|
||||||
|
match:float = false
|
||||||
|
match:workspace = n[s:window] f[1]
|
||||||
|
|
||||||
|
border_size = 0
|
||||||
|
rounding = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = wr-kitty-stuff
|
||||||
|
match:class = wr_kitty
|
||||||
|
|
||||||
|
float = true
|
||||||
|
size = 200 200
|
||||||
|
pin = false
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = tagged-kitty-floats
|
||||||
|
match:tag = tag_kitty
|
||||||
|
|
||||||
|
float = true
|
||||||
|
}
|
||||||
|
|
||||||
|
windowrule {
|
||||||
|
name = static-kitty-tag
|
||||||
|
match:class = tag_kitty
|
||||||
|
|
||||||
|
tag = +tag_kitty
|
||||||
|
}
|
||||||
|
|
||||||
gesture = 3, left, dispatcher, exec, kitty
|
gesture = 3, left, dispatcher, exec, kitty
|
||||||
gesture = 3, right, float
|
gesture = 3, right, float
|
||||||
|
|
@ -357,6 +410,4 @@ gesture = 5, right, dispatcher, sendshortcut, , t, activewindow
|
||||||
gesture = 4, right, dispatcher, sendshortcut, , return, activewindow
|
gesture = 4, right, dispatcher, sendshortcut, , return, activewindow
|
||||||
gesture = 4, left, dispatcher, movecursortocorner, 1
|
gesture = 4, left, dispatcher, movecursortocorner, 1
|
||||||
|
|
||||||
windowrule = float, pin, class:wr_kitty
|
gesturep = 2, right, float
|
||||||
windowrule = size 200 200, class:wr_kitty
|
|
||||||
windowrule = unset pin, class:wr_kitty
|
|
||||||
|
|
|
||||||
151
meson.build
151
meson.build
|
|
@ -1,151 +0,0 @@
|
||||||
project(
|
|
||||||
'Hyprland',
|
|
||||||
'cpp',
|
|
||||||
'c',
|
|
||||||
version: run_command('cat', join_paths(meson.project_source_root(), 'VERSION'), check: true).stdout().strip(),
|
|
||||||
default_options: [
|
|
||||||
'warning_level=2',
|
|
||||||
'default_library=static',
|
|
||||||
'optimization=3',
|
|
||||||
'buildtype=release',
|
|
||||||
'debug=false',
|
|
||||||
'b_lto=false',
|
|
||||||
'cpp_std=c++26',
|
|
||||||
],
|
|
||||||
meson_version: '>= 1.1.0',
|
|
||||||
)
|
|
||||||
|
|
||||||
datarootdir = '-DDATAROOTDIR="' + get_option('prefix') / get_option('datadir') + '"'
|
|
||||||
add_project_arguments(
|
|
||||||
[
|
|
||||||
'-Wno-unused-parameter',
|
|
||||||
'-Wno-unused-value',
|
|
||||||
'-Wno-missing-field-initializers',
|
|
||||||
'-Wno-narrowing',
|
|
||||||
'-Wno-pointer-arith',
|
|
||||||
datarootdir,
|
|
||||||
'-DHYPRLAND_VERSION="' + meson.project_version() + '"',
|
|
||||||
],
|
|
||||||
language: 'cpp',
|
|
||||||
)
|
|
||||||
|
|
||||||
cpp_compiler = meson.get_compiler('cpp')
|
|
||||||
if cpp_compiler.check_header('execinfo.h')
|
|
||||||
add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
|
|
||||||
endif
|
|
||||||
|
|
||||||
aquamarine = dependency('aquamarine', version: '>=0.9.3')
|
|
||||||
hyprcursor = dependency('hyprcursor', version: '>=0.1.7')
|
|
||||||
hyprgraphics = dependency('hyprgraphics', version: '>=0.1.6')
|
|
||||||
hyprlang = dependency('hyprlang', version: '>=0.3.2')
|
|
||||||
hyprutils = dependency('hyprutils', version: '>=0.8.2')
|
|
||||||
|
|
||||||
aq_ver_list = aquamarine.version().split('.')
|
|
||||||
git = find_program('git', required: false)
|
|
||||||
|
|
||||||
if git.found()
|
|
||||||
git_hash = run_command(git, 'rev-parse', 'HEAD').stdout().strip()
|
|
||||||
git_branch = run_command(git, 'branch', '--show-current').stdout().strip()
|
|
||||||
git_message = run_command(git, 'show', '-s', '--format=%s').stdout().strip()
|
|
||||||
git_date = run_command(git, 'show', '-s', '--format=%cd', '--date=local').stdout().strip()
|
|
||||||
git_dirty = run_command(git, 'diff-index', '--quiet', 'HEAD', '--', check: false).returncode() != 0 ? 'dirty' : 'clean'
|
|
||||||
git_tag = run_command(git, 'describe', '--tags').stdout().strip()
|
|
||||||
git_commits = run_command(git, 'rev-list', '--count', 'HEAD').stdout().strip()
|
|
||||||
else
|
|
||||||
git_hash = 'unknown'
|
|
||||||
git_branch = 'unknown'
|
|
||||||
git_message = 'unknown'
|
|
||||||
git_date = 'unknown'
|
|
||||||
git_dirty = 'unknown'
|
|
||||||
git_tag = 'unknown'
|
|
||||||
git_commits = '0'
|
|
||||||
endif
|
|
||||||
|
|
||||||
cfg = configuration_data()
|
|
||||||
cfg.set('GIT_COMMIT_HASH', git_hash)
|
|
||||||
cfg.set('GIT_BRANCH', git_branch)
|
|
||||||
cfg.set('GIT_COMMIT_MESSAGE', git_message)
|
|
||||||
cfg.set('GIT_COMMIT_DATE', git_date)
|
|
||||||
cfg.set('GIT_DIRTY', git_dirty)
|
|
||||||
cfg.set('GIT_TAG', git_tag)
|
|
||||||
cfg.set('GIT_COMMITS', git_commits)
|
|
||||||
cfg.set('AQUAMARINE_VERSION', aquamarine.version())
|
|
||||||
cfg.set('AQUAMARINE_VERSION_MAJOR', aq_ver_list[0])
|
|
||||||
cfg.set('AQUAMARINE_VERSION_MINOR', aq_ver_list[1])
|
|
||||||
cfg.set('AQUAMARINE_VERSION_PATCH', aq_ver_list[2])
|
|
||||||
cfg.set('HYPRLANG_VERSION', hyprlang.version())
|
|
||||||
cfg.set('HYPRUTILS_VERSION', hyprutils.version())
|
|
||||||
cfg.set('HYPRCURSOR_VERSION', hyprcursor.version())
|
|
||||||
cfg.set('HYPRGRAPHICS_VERSION', hyprgraphics.version())
|
|
||||||
|
|
||||||
version_h = configure_file(
|
|
||||||
input: 'src/version.h.in',
|
|
||||||
output: 'version.h',
|
|
||||||
configuration: cfg
|
|
||||||
)
|
|
||||||
|
|
||||||
install_headers(version_h, subdir: 'src')
|
|
||||||
|
|
||||||
xcb_dep = dependency('xcb', required: get_option('xwayland'))
|
|
||||||
xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland'))
|
|
||||||
xcb_errors_dep = dependency('xcb-errors', required: get_option('xwayland'))
|
|
||||||
xcb_icccm_dep = dependency('xcb-icccm', required: get_option('xwayland'))
|
|
||||||
xcb_render_dep = dependency('xcb-render', required: get_option('xwayland'))
|
|
||||||
xcb_res_dep = dependency('xcb-res', required: get_option('xwayland'))
|
|
||||||
xcb_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland'))
|
|
||||||
gio_dep = dependency('gio-2.0', required: true)
|
|
||||||
|
|
||||||
if not xcb_dep.found()
|
|
||||||
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
|
|
||||||
endif
|
|
||||||
|
|
||||||
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
|
|
||||||
epoll_dep = dependency('epoll-shim', required: false)
|
|
||||||
inotify_dep = dependency('libinotify', required: false)
|
|
||||||
re2 = dependency('re2', required: true)
|
|
||||||
|
|
||||||
systemd_option = get_option('systemd')
|
|
||||||
systemd = dependency('systemd', required: systemd_option)
|
|
||||||
systemd_option.enable_auto_if(systemd.found())
|
|
||||||
if (systemd_option.enabled())
|
|
||||||
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
|
|
||||||
subdir('systemd')
|
|
||||||
endif
|
|
||||||
|
|
||||||
if get_option('buildtype') == 'debug'
|
|
||||||
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
|
|
||||||
endif
|
|
||||||
|
|
||||||
run_command('sh', '-c', 'scripts/generateShaderIncludes.sh', check: true)
|
|
||||||
|
|
||||||
globber = run_command('find', 'src', '-name', '*.h*', '-o', '-name', '*.inc', check: true)
|
|
||||||
headers = globber.stdout().strip().split('\n')
|
|
||||||
foreach file : headers
|
|
||||||
install_headers(file, subdir: 'hyprland', preserve_path: true)
|
|
||||||
endforeach
|
|
||||||
install_headers(version_h, subdir: 'src')
|
|
||||||
|
|
||||||
tracy = dependency('tracy', static: true, required: get_option('tracy_enable'))
|
|
||||||
if get_option('tracy_enable') and get_option('buildtype') != 'debugoptimized'
|
|
||||||
warning('Profiling builds should set -- buildtype = debugoptimized')
|
|
||||||
endif
|
|
||||||
|
|
||||||
subdir('protocols')
|
|
||||||
subdir('src')
|
|
||||||
subdir('hyprctl')
|
|
||||||
subdir('assets')
|
|
||||||
subdir('example')
|
|
||||||
subdir('docs')
|
|
||||||
if get_option('hyprpm').enabled()
|
|
||||||
subdir('hyprpm/src')
|
|
||||||
endif
|
|
||||||
|
|
||||||
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')
|
|
||||||
import('pkgconfig').generate(
|
|
||||||
name: 'Hyprland',
|
|
||||||
filebase: 'hyprland',
|
|
||||||
url: 'https://github.com/hyprwm/Hyprland',
|
|
||||||
description: 'Hyprland header files',
|
|
||||||
install_dir: pkg_install_dir,
|
|
||||||
subdirs: ['', 'hyprland/protocols', 'hyprland'],
|
|
||||||
)
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
|
|
||||||
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
|
|
||||||
option('uwsm', type: 'feature', value: 'enabled', description: 'Enable uwsm integration (only if systemd is enabled)')
|
|
||||||
option('hyprpm', type: 'feature', value: 'enabled', description: 'Enable hyprpm')
|
|
||||||
option('tracy_enable', type: 'boolean', value: false , description: 'Enable profiling')
|
|
||||||
115
nix/default.nix
115
nix/default.nix
|
|
@ -11,7 +11,9 @@
|
||||||
cairo,
|
cairo,
|
||||||
epoll-shim,
|
epoll-shim,
|
||||||
git,
|
git,
|
||||||
glaze,
|
glaze-hyprland,
|
||||||
|
glslang,
|
||||||
|
gtest,
|
||||||
hyprcursor,
|
hyprcursor,
|
||||||
hyprgraphics,
|
hyprgraphics,
|
||||||
hyprland-protocols,
|
hyprland-protocols,
|
||||||
|
|
@ -19,13 +21,22 @@
|
||||||
hyprlang,
|
hyprlang,
|
||||||
hyprutils,
|
hyprutils,
|
||||||
hyprwayland-scanner,
|
hyprwayland-scanner,
|
||||||
|
hyprwire,
|
||||||
|
lcms2,
|
||||||
libGL,
|
libGL,
|
||||||
libdrm,
|
libdrm,
|
||||||
libexecinfo,
|
libexecinfo,
|
||||||
libinput,
|
libinput,
|
||||||
|
libxcb,
|
||||||
|
libxcb-errors,
|
||||||
|
libxcb-render-util,
|
||||||
|
libxcb-wm,
|
||||||
|
libxdmcp,
|
||||||
|
libxcursor,
|
||||||
libxkbcommon,
|
libxkbcommon,
|
||||||
libuuid,
|
libuuid,
|
||||||
libgbm,
|
libgbm,
|
||||||
|
muparser,
|
||||||
pango,
|
pango,
|
||||||
pciutils,
|
pciutils,
|
||||||
re2,
|
re2,
|
||||||
|
|
@ -35,9 +46,9 @@
|
||||||
wayland,
|
wayland,
|
||||||
wayland-protocols,
|
wayland-protocols,
|
||||||
wayland-scanner,
|
wayland-scanner,
|
||||||
xorg,
|
|
||||||
xwayland,
|
xwayland,
|
||||||
debug ? false,
|
debug ? false,
|
||||||
|
withTests ? false,
|
||||||
enableXWayland ? true,
|
enableXWayland ? true,
|
||||||
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
|
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
|
||||||
wrapRuntimeDeps ? true,
|
wrapRuntimeDeps ? true,
|
||||||
|
|
@ -45,18 +56,30 @@
|
||||||
commit,
|
commit,
|
||||||
revCount,
|
revCount,
|
||||||
date,
|
date,
|
||||||
withHyprtester ? false,
|
|
||||||
# deprecated flags
|
# deprecated flags
|
||||||
enableNvidiaPatches ? false,
|
enableNvidiaPatches ? false,
|
||||||
nvidiaPatches ? false,
|
nvidiaPatches ? false,
|
||||||
hidpiXWayland ? false,
|
hidpiXWayland ? false,
|
||||||
legacyRenderer ? false,
|
legacyRenderer ? false,
|
||||||
|
withHyprtester ? false,
|
||||||
}: let
|
}: let
|
||||||
inherit (builtins) foldl' readFile;
|
inherit (builtins) foldl' readFile;
|
||||||
inherit (lib.asserts) assertMsg;
|
inherit (lib.asserts) assertMsg;
|
||||||
inherit (lib.attrsets) mapAttrsToList;
|
inherit (lib.attrsets) mapAttrsToList;
|
||||||
inherit (lib.lists) flatten concatLists optional optionals;
|
inherit
|
||||||
inherit (lib.strings) makeBinPath optionalString cmakeBool trim;
|
(lib.lists)
|
||||||
|
flatten
|
||||||
|
concatLists
|
||||||
|
optional
|
||||||
|
optionals
|
||||||
|
;
|
||||||
|
inherit
|
||||||
|
(lib.strings)
|
||||||
|
makeBinPath
|
||||||
|
optionalString
|
||||||
|
cmakeBool
|
||||||
|
trim
|
||||||
|
;
|
||||||
fs = lib.fileset;
|
fs = lib.fileset;
|
||||||
|
|
||||||
adapters = flatten [
|
adapters = flatten [
|
||||||
|
|
@ -68,11 +91,13 @@
|
||||||
in
|
in
|
||||||
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
|
assert assertMsg (!nvidiaPatches) "The option `nvidiaPatches` has been removed.";
|
||||||
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
|
assert assertMsg (!enableNvidiaPatches) "The option `enableNvidiaPatches` has been removed.";
|
||||||
assert assertMsg (!hidpiXWayland) "The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland";
|
assert assertMsg (!hidpiXWayland)
|
||||||
|
"The option `hidpiXWayland` has been removed. Please refer https://wiki.hypr.land/Configuring/XWayland";
|
||||||
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
|
assert assertMsg (!legacyRenderer) "The option `legacyRenderer` has been removed. Legacy renderer is no longer supported.";
|
||||||
|
assert assertMsg (!withHyprtester) "The option `withHyprtester` has been removed. Hyprtester is always built now.";
|
||||||
customStdenv.mkDerivation (finalAttrs: {
|
customStdenv.mkDerivation (finalAttrs: {
|
||||||
pname = "hyprland${optionalString debug "-debug"}";
|
pname = "hyprland${optionalString debug "-debug"}";
|
||||||
inherit version;
|
inherit version withTests;
|
||||||
|
|
||||||
src = fs.toSource {
|
src = fs.toSource {
|
||||||
root = ../.;
|
root = ../.;
|
||||||
|
|
@ -80,37 +105,50 @@ in
|
||||||
fs.intersection
|
fs.intersection
|
||||||
# allows non-flake builds to only include files tracked by git
|
# allows non-flake builds to only include files tracked by git
|
||||||
(fs.gitTracked ../.)
|
(fs.gitTracked ../.)
|
||||||
(fs.unions (flatten [
|
(
|
||||||
|
fs.unions (flatten [
|
||||||
../assets/hyprland-portals.conf
|
../assets/hyprland-portals.conf
|
||||||
../assets/install
|
../assets/install
|
||||||
../hyprctl
|
../hyprctl
|
||||||
../hyprland.pc.in
|
../hyprland.pc.in
|
||||||
|
../hyprpm
|
||||||
../LICENSE
|
../LICENSE
|
||||||
../protocols
|
../protocols
|
||||||
../src
|
../src
|
||||||
|
../start
|
||||||
../systemd
|
../systemd
|
||||||
../VERSION
|
../VERSION
|
||||||
(fs.fileFilter (file: file.hasExt "1") ../docs)
|
(fs.fileFilter (file: file.hasExt "1") ../docs)
|
||||||
(fs.fileFilter (file: file.hasExt "conf" || file.hasExt "desktop") ../example)
|
(fs.fileFilter (file: file.hasExt "conf" || file.hasExt "in") ../example)
|
||||||
(fs.fileFilter (file: file.hasExt "sh") ../scripts)
|
(fs.fileFilter (file: file.hasExt "sh") ../scripts)
|
||||||
(fs.fileFilter (file: file.name == "CMakeLists.txt") ../.)
|
(fs.fileFilter (file: file.name == "CMakeLists.txt") ../.)
|
||||||
(optional withHyprtester ../hyprtester)
|
(optional withTests [
|
||||||
]));
|
../tests
|
||||||
|
../hyprtester
|
||||||
|
])
|
||||||
|
])
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
postPatch = ''
|
postPatch = ''
|
||||||
# Fix hardcoded paths to /usr installation
|
# Fix hardcoded paths to /usr installation
|
||||||
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
|
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
|
||||||
|
|
||||||
# Remove extra @PREFIX@ to fix pkg-config paths
|
# Remove extra @PREFIX@ to fix some paths
|
||||||
sed -i "s#@PREFIX@/##g" hyprland.pc.in
|
sed -i "s#@PREFIX@/##g" hyprland.pc.in
|
||||||
|
sed -i "s#@PREFIX@/##g" example/hyprland.desktop.in
|
||||||
'';
|
'';
|
||||||
|
|
||||||
COMMITS = revCount;
|
env = {
|
||||||
DATE = date;
|
GIT_COMMITS = revCount;
|
||||||
DIRTY = optionalString (commit == "") "dirty";
|
GIT_COMMIT_DATE = date;
|
||||||
HASH = commit;
|
GIT_COMMIT_HASH = commit;
|
||||||
TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
|
GIT_DIRTY =
|
||||||
|
if (commit == "")
|
||||||
|
then "clean"
|
||||||
|
else "dirty";
|
||||||
|
GIT_TAG = "v${trim (readFile "${finalAttrs.src}/VERSION")}";
|
||||||
|
};
|
||||||
|
|
||||||
depsBuildBuild = [
|
depsBuildBuild = [
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
@ -118,6 +156,7 @@ in
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
hyprwayland-scanner
|
hyprwayland-scanner
|
||||||
|
hyprwire
|
||||||
makeWrapper
|
makeWrapper
|
||||||
cmake
|
cmake
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
@ -134,18 +173,24 @@ in
|
||||||
aquamarine
|
aquamarine
|
||||||
cairo
|
cairo
|
||||||
git
|
git
|
||||||
glaze
|
glaze-hyprland
|
||||||
|
glslang
|
||||||
|
gtest
|
||||||
hyprcursor
|
hyprcursor
|
||||||
hyprgraphics
|
hyprgraphics
|
||||||
hyprland-protocols
|
hyprland-protocols
|
||||||
hyprlang
|
hyprlang
|
||||||
hyprutils
|
hyprutils
|
||||||
|
hyprwire
|
||||||
|
lcms2
|
||||||
libdrm
|
libdrm
|
||||||
|
libgbm
|
||||||
libGL
|
libGL
|
||||||
libinput
|
libinput
|
||||||
libuuid
|
libuuid
|
||||||
|
libxcursor
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
libgbm
|
muparser
|
||||||
pango
|
pango
|
||||||
pciutils
|
pciutils
|
||||||
re2
|
re2
|
||||||
|
|
@ -154,16 +199,15 @@ in
|
||||||
wayland
|
wayland
|
||||||
wayland-protocols
|
wayland-protocols
|
||||||
wayland-scanner
|
wayland-scanner
|
||||||
xorg.libXcursor
|
|
||||||
]
|
]
|
||||||
(optionals customStdenv.hostPlatform.isBSD [epoll-shim])
|
(optionals customStdenv.hostPlatform.isBSD [epoll-shim])
|
||||||
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
|
(optionals customStdenv.hostPlatform.isMusl [libexecinfo])
|
||||||
(optionals enableXWayland [
|
(optionals enableXWayland [
|
||||||
xorg.libxcb
|
libxcb
|
||||||
xorg.libXdmcp
|
libxcb-errors
|
||||||
xorg.xcbutilerrors
|
libxcb-render-util
|
||||||
xorg.xcbutilrenderutil
|
libxcb-wm
|
||||||
xorg.xcbutilwm
|
libxdmcp
|
||||||
xwayland
|
xwayland
|
||||||
])
|
])
|
||||||
(optional withSystemd systemd)
|
(optional withSystemd systemd)
|
||||||
|
|
@ -180,14 +224,14 @@ in
|
||||||
dontStrip = debug;
|
dontStrip = debug;
|
||||||
|
|
||||||
cmakeFlags = mapAttrsToList cmakeBool {
|
cmakeFlags = mapAttrsToList cmakeBool {
|
||||||
|
"BUILT_WITH_NIX" = true;
|
||||||
"NO_XWAYLAND" = !enableXWayland;
|
"NO_XWAYLAND" = !enableXWayland;
|
||||||
"LEGACY_RENDERER" = legacyRenderer;
|
"LEGACY_RENDERER" = legacyRenderer;
|
||||||
"NO_SYSTEMD" = !withSystemd;
|
"NO_SYSTEMD" = !withSystemd;
|
||||||
"CMAKE_DISABLE_PRECOMPILE_HEADERS" = true;
|
"CMAKE_DISABLE_PRECOMPILE_HEADERS" = true;
|
||||||
"NO_UWSM" = true;
|
"NO_UWSM" = !withSystemd;
|
||||||
"NO_HYPRPM" = true;
|
|
||||||
"TRACY_ENABLE" = false;
|
"TRACY_ENABLE" = false;
|
||||||
"BUILD_HYPRTESTER" = withHyprtester;
|
"WITH_TESTS" = withTests;
|
||||||
};
|
};
|
||||||
|
|
||||||
preConfigure = ''
|
preConfigure = ''
|
||||||
|
|
@ -199,19 +243,26 @@ in
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
${optionalString wrapRuntimeDeps ''
|
${optionalString wrapRuntimeDeps ''
|
||||||
wrapProgram $out/bin/Hyprland \
|
wrapProgram $out/bin/Hyprland \
|
||||||
--suffix PATH : ${makeBinPath [
|
--suffix PATH : ${
|
||||||
|
makeBinPath [
|
||||||
binutils
|
binutils
|
||||||
hyprland-guiutils
|
hyprland-guiutils
|
||||||
pciutils
|
pciutils
|
||||||
pkgconf
|
pkgconf
|
||||||
]}
|
]
|
||||||
|
}
|
||||||
''}
|
''}
|
||||||
'' + optionalString withHyprtester ''
|
|
||||||
|
${optionalString withTests ''
|
||||||
install hyprtester/pointer-warp -t $out/bin
|
install hyprtester/pointer-warp -t $out/bin
|
||||||
install hyprtester/pointer-scroll -t $out/bin
|
install hyprtester/pointer-scroll -t $out/bin
|
||||||
|
install hyprtester/shortcut-inhibitor -t $out/bin
|
||||||
|
install hyprland_gtests -t $out/bin
|
||||||
|
install hyprtester/child-window -t $out/bin
|
||||||
|
''}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
passthru.providedSessions = ["hyprland"];
|
passthru.providedSessions = ["hyprland"] ++ optionals withSystemd ["hyprland-uwsm"];
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
homepage = "https://github.com/hyprwm/Hyprland";
|
homepage = "https://github.com/hyprwm/Hyprland";
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
writeShellApplication,
|
writeShellApplication,
|
||||||
deadnix,
|
deadnix,
|
||||||
statix,
|
statix,
|
||||||
alejandra,
|
nixfmt,
|
||||||
llvmPackages_19,
|
llvmPackages_19,
|
||||||
fd,
|
fd,
|
||||||
}:
|
}:
|
||||||
|
|
@ -11,7 +11,7 @@ writeShellApplication {
|
||||||
runtimeInputs = [
|
runtimeInputs = [
|
||||||
deadnix
|
deadnix
|
||||||
statix
|
statix
|
||||||
alejandra
|
nixfmt
|
||||||
llvmPackages_19.clang-tools
|
llvmPackages_19.clang-tools
|
||||||
fd
|
fd
|
||||||
];
|
];
|
||||||
|
|
@ -24,14 +24,14 @@ writeShellApplication {
|
||||||
nix_format() {
|
nix_format() {
|
||||||
if [ "$*" = 0 ]; then
|
if [ "$*" = 0 ]; then
|
||||||
fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
|
fd '.*\.nix' . -E "$excludes" -x statix fix -- {} \;
|
||||||
fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X alejandra {} \;
|
fd '.*\.nix' . -E "$excludes" -X deadnix -e -- {} \; -X nixfmt {} \;
|
||||||
elif [ -d "$1" ]; then
|
elif [ -d "$1" ]; then
|
||||||
fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
|
fd '.*\.nix' "$1" -E "$excludes" -i -x statix fix -- {} \;
|
||||||
fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X alejandra {} \;
|
fd '.*\.nix' "$1" -E "$excludes" -i -X deadnix -e -- {} \; -X nixfmt {} \;
|
||||||
else
|
else
|
||||||
statix fix -- "$1"
|
statix fix -- "$1"
|
||||||
deadnix -e "$1"
|
deadnix -e "$1"
|
||||||
alejandra "$1"
|
nixfmt "$1"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
self: {
|
self:
|
||||||
config,
|
{
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: let
|
}:
|
||||||
|
let
|
||||||
inherit (pkgs.stdenv.hostPlatform) system;
|
inherit (pkgs.stdenv.hostPlatform) system;
|
||||||
|
|
||||||
package = self.packages.${system}.default;
|
package = self.packages.${system}.default;
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
config = {
|
config = {
|
||||||
wayland.windowManager.hyprland.package = lib.mkDefault package;
|
wayland.windowManager.hyprland.package = lib.mkDefault package;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
51
nix/lib.nix
51
nix/lib.nix
|
|
@ -1,4 +1,5 @@
|
||||||
lib: let
|
lib:
|
||||||
|
let
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
attrNames
|
attrNames
|
||||||
filterAttrs
|
filterAttrs
|
||||||
|
|
@ -81,15 +82,23 @@ lib: let
|
||||||
|
|
||||||
:::
|
:::
|
||||||
*/
|
*/
|
||||||
toHyprlang = {
|
toHyprlang =
|
||||||
topCommandsPrefixes ? ["$" "bezier"],
|
{
|
||||||
bottomCommandsPrefixes ? [],
|
topCommandsPrefixes ? [
|
||||||
}: attrs: let
|
"$"
|
||||||
toHyprlang' = attrs: let
|
"bezier"
|
||||||
|
],
|
||||||
|
bottomCommandsPrefixes ? [ ],
|
||||||
|
}:
|
||||||
|
attrs:
|
||||||
|
let
|
||||||
|
toHyprlang' =
|
||||||
|
attrs:
|
||||||
|
let
|
||||||
# Specially configured `toKeyValue` generator with support for duplicate keys
|
# Specially configured `toKeyValue` generator with support for duplicate keys
|
||||||
# and a legible key-value separator.
|
# and a legible key-value separator.
|
||||||
mkCommands = generators.toKeyValue {
|
mkCommands = generators.toKeyValue {
|
||||||
mkKeyValue = generators.mkKeyValueDefault {} " = ";
|
mkKeyValue = generators.mkKeyValueDefault { } " = ";
|
||||||
listsAsDuplicateKeys = true;
|
listsAsDuplicateKeys = true;
|
||||||
indent = ""; # No indent, since we don't have nesting
|
indent = ""; # No indent, since we don't have nesting
|
||||||
};
|
};
|
||||||
|
|
@ -99,8 +108,7 @@ lib: let
|
||||||
commands = flattenAttrs (p: k: "${p}:${k}") attrs;
|
commands = flattenAttrs (p: k: "${p}:${k}") attrs;
|
||||||
|
|
||||||
# General filtering function to check if a key starts with any prefix in a given list.
|
# General filtering function to check if a key starts with any prefix in a given list.
|
||||||
filterCommands = list: n:
|
filterCommands = list: n: foldl (acc: prefix: acc || hasPrefix prefix n) false list;
|
||||||
foldl (acc: prefix: acc || hasPrefix prefix n) false list;
|
|
||||||
|
|
||||||
# Partition keys into top commands and the rest
|
# Partition keys into top commands and the rest
|
||||||
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
|
result = partition (filterCommands topCommandsPrefixes) (attrNames commands);
|
||||||
|
|
@ -174,25 +182,20 @@ lib: let
|
||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
*/
|
*/
|
||||||
flattenAttrs = pred: attrs: let
|
flattenAttrs =
|
||||||
flattenAttrs' = prefix: attrs:
|
pred: attrs:
|
||||||
|
let
|
||||||
|
flattenAttrs' =
|
||||||
|
prefix: attrs:
|
||||||
builtins.foldl' (
|
builtins.foldl' (
|
||||||
acc: key: let
|
acc: key:
|
||||||
|
let
|
||||||
value = attrs.${key};
|
value = attrs.${key};
|
||||||
newKey =
|
newKey = if prefix == "" then key else pred prefix key;
|
||||||
if prefix == ""
|
|
||||||
then key
|
|
||||||
else pred prefix key;
|
|
||||||
in
|
in
|
||||||
acc
|
acc // (if builtins.isAttrs value then flattenAttrs' newKey value else { "${newKey}" = value; })
|
||||||
// (
|
) { } (builtins.attrNames attrs);
|
||||||
if builtins.isAttrs value
|
|
||||||
then flattenAttrs' newKey value
|
|
||||||
else {"${newKey}" = value;}
|
|
||||||
)
|
|
||||||
) {} (builtins.attrNames attrs);
|
|
||||||
in
|
in
|
||||||
flattenAttrs' "" attrs;
|
flattenAttrs' "" attrs;
|
||||||
in
|
in
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,21 @@
|
||||||
inputs: {
|
inputs:
|
||||||
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: let
|
}:
|
||||||
|
let
|
||||||
inherit (pkgs.stdenv.hostPlatform) system;
|
inherit (pkgs.stdenv.hostPlatform) system;
|
||||||
selflib = import ./lib.nix lib;
|
selflib = import ./lib.nix lib;
|
||||||
cfg = config.programs.hyprland;
|
cfg = config.programs.hyprland;
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
options = {
|
options = {
|
||||||
programs.hyprland = {
|
programs.hyprland = {
|
||||||
plugins = lib.mkOption {
|
plugins = lib.mkOption {
|
||||||
type = with lib.types; listOf (either package path);
|
type = with lib.types; listOf (either package path);
|
||||||
default = [];
|
default = [ ];
|
||||||
description = ''
|
description = ''
|
||||||
List of Hyprland plugins to use. Can either be packages or
|
List of Hyprland plugins to use. Can either be packages or
|
||||||
absolute plugin paths.
|
absolute plugin paths.
|
||||||
|
|
@ -20,7 +23,9 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
settings = lib.mkOption {
|
settings = lib.mkOption {
|
||||||
type = with lib.types; let
|
type =
|
||||||
|
with lib.types;
|
||||||
|
let
|
||||||
valueType =
|
valueType =
|
||||||
nullOr (oneOf [
|
nullOr (oneOf [
|
||||||
bool
|
bool
|
||||||
|
|
@ -36,7 +41,7 @@ in {
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
valueType;
|
valueType;
|
||||||
default = {};
|
default = { };
|
||||||
description = ''
|
description = ''
|
||||||
Hyprland configuration written in Nix. Entries with the same key
|
Hyprland configuration written in Nix. Entries with the same key
|
||||||
should be written as lists. Variables' and colors' names should be
|
should be written as lists. Variables' and colors' names should be
|
||||||
|
|
@ -92,8 +97,15 @@ in {
|
||||||
|
|
||||||
topPrefixes = lib.mkOption {
|
topPrefixes = lib.mkOption {
|
||||||
type = with lib.types; listOf str;
|
type = with lib.types; listOf str;
|
||||||
default = ["$" "bezier"];
|
default = [
|
||||||
example = ["$" "bezier" "source"];
|
"$"
|
||||||
|
"bezier"
|
||||||
|
];
|
||||||
|
example = [
|
||||||
|
"$"
|
||||||
|
"bezier"
|
||||||
|
"source"
|
||||||
|
];
|
||||||
description = ''
|
description = ''
|
||||||
List of prefix of attributes to put at the top of the config.
|
List of prefix of attributes to put at the top of the config.
|
||||||
'';
|
'';
|
||||||
|
|
@ -101,8 +113,8 @@ in {
|
||||||
|
|
||||||
bottomPrefixes = lib.mkOption {
|
bottomPrefixes = lib.mkOption {
|
||||||
type = with lib.types; listOf str;
|
type = with lib.types; listOf str;
|
||||||
default = [];
|
default = [ ];
|
||||||
example = ["source"];
|
example = [ "source" ];
|
||||||
description = ''
|
description = ''
|
||||||
List of prefix of attributes to put at the bottom of the config.
|
List of prefix of attributes to put at the bottom of the config.
|
||||||
'';
|
'';
|
||||||
|
|
@ -117,20 +129,22 @@ in {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
(lib.mkIf cfg.enable {
|
(lib.mkIf cfg.enable {
|
||||||
environment.etc."xdg/hypr/hyprland.conf" = let
|
environment.etc."xdg/hypr/hyprland.conf" =
|
||||||
shouldGenerate = cfg.extraConfig != "" || cfg.settings != {} || cfg.plugins != [];
|
let
|
||||||
|
shouldGenerate = cfg.extraConfig != "" || cfg.settings != { } || cfg.plugins != [ ];
|
||||||
|
|
||||||
pluginsToHyprlang = plugins:
|
pluginsToHyprlang =
|
||||||
selflib.toHyprlang {
|
_plugins:
|
||||||
|
selflib.toHyprlang
|
||||||
|
{
|
||||||
topCommandsPrefixes = cfg.topPrefixes;
|
topCommandsPrefixes = cfg.topPrefixes;
|
||||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
"exec-once" = let
|
"exec-once" =
|
||||||
mkEntry = entry:
|
let
|
||||||
if lib.types.package.check entry
|
mkEntry =
|
||||||
then "${entry}/lib/lib${entry.pname}.so"
|
entry: if lib.types.package.check entry then "${entry}/lib/lib${entry.pname}.so" else entry;
|
||||||
else entry;
|
|
||||||
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
|
hyprctl = lib.getExe' config.programs.hyprland.package "hyprctl";
|
||||||
in
|
in
|
||||||
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
|
map (p: "${hyprctl} plugin load ${mkEntry p}") cfg.plugins;
|
||||||
|
|
@ -138,14 +152,13 @@ in {
|
||||||
in
|
in
|
||||||
lib.mkIf shouldGenerate {
|
lib.mkIf shouldGenerate {
|
||||||
text =
|
text =
|
||||||
lib.optionalString (cfg.plugins != [])
|
lib.optionalString (cfg.plugins != [ ]) (pluginsToHyprlang cfg.plugins)
|
||||||
(pluginsToHyprlang cfg.plugins)
|
+ lib.optionalString (cfg.settings != { }) (
|
||||||
+ lib.optionalString (cfg.settings != {})
|
selflib.toHyprlang {
|
||||||
(selflib.toHyprlang {
|
|
||||||
topCommandsPrefixes = cfg.topPrefixes;
|
topCommandsPrefixes = cfg.topPrefixes;
|
||||||
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
bottomCommandsPrefixes = cfg.bottomPrefixes;
|
||||||
}
|
} cfg.settings
|
||||||
cfg.settings)
|
)
|
||||||
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
|
+ lib.optionalString (cfg.extraConfig != "") cfg.extraConfig;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,27 @@
|
||||||
self,
|
self,
|
||||||
lib,
|
lib,
|
||||||
inputs,
|
inputs,
|
||||||
}: let
|
}:
|
||||||
mkDate = longDate: (lib.concatStringsSep "-" [
|
let
|
||||||
|
mkDate =
|
||||||
|
longDate:
|
||||||
|
(lib.concatStringsSep "-" [
|
||||||
(builtins.substring 0 4 longDate)
|
(builtins.substring 0 4 longDate)
|
||||||
(builtins.substring 4 2 longDate)
|
(builtins.substring 4 2 longDate)
|
||||||
(builtins.substring 6 2 longDate)
|
(builtins.substring 6 2 longDate)
|
||||||
]);
|
]);
|
||||||
ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
ver = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
# Contains what a user is most likely to care about:
|
# Contains what a user is most likely to care about:
|
||||||
# Hyprland itself, XDPH and the Share Picker.
|
# Hyprland itself, XDPH and the Share Picker.
|
||||||
default = lib.composeManyExtensions (with self.overlays; [
|
default = lib.composeManyExtensions (
|
||||||
|
with self.overlays;
|
||||||
|
[
|
||||||
hyprland-packages
|
hyprland-packages
|
||||||
hyprland-extras
|
hyprland-extras
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
|
|
||||||
# Packages for variations of Hyprland, dependencies included.
|
# Packages for variations of Hyprland, dependencies included.
|
||||||
hyprland-packages = lib.composeManyExtensions [
|
hyprland-packages = lib.composeManyExtensions [
|
||||||
|
|
@ -28,45 +35,50 @@ in {
|
||||||
inputs.hyprlang.overlays.default
|
inputs.hyprlang.overlays.default
|
||||||
inputs.hyprutils.overlays.default
|
inputs.hyprutils.overlays.default
|
||||||
inputs.hyprwayland-scanner.overlays.default
|
inputs.hyprwayland-scanner.overlays.default
|
||||||
|
inputs.hyprwire.overlays.default
|
||||||
self.overlays.udis86
|
self.overlays.udis86
|
||||||
|
self.overlays.glaze
|
||||||
|
|
||||||
# Hyprland packages themselves
|
# Hyprland packages themselves
|
||||||
(final: _prev: let
|
(
|
||||||
|
final: _prev:
|
||||||
|
let
|
||||||
date = mkDate (self.lastModifiedDate or "19700101");
|
date = mkDate (self.lastModifiedDate or "19700101");
|
||||||
version = "${ver}+date=${date}_${self.shortRev or "dirty"}";
|
version = "${ver}+date=${date}_${self.shortRev or "dirty"}";
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
hyprland = final.callPackage ./default.nix {
|
hyprland = final.callPackage ./default.nix {
|
||||||
stdenv = final.gcc15Stdenv;
|
stdenv = final.gcc15Stdenv;
|
||||||
commit = self.rev or "";
|
commit = self.rev or "";
|
||||||
revCount = self.sourceInfo.revCount or "";
|
revCount = self.sourceInfo.revCount or "";
|
||||||
inherit date version;
|
inherit date version;
|
||||||
};
|
};
|
||||||
hyprland-unwrapped = final.hyprland.override {wrapRuntimeDeps = false;};
|
hyprland-unwrapped = final.hyprland.override { wrapRuntimeDeps = false; };
|
||||||
|
|
||||||
hyprland-with-hyprtester = final.hyprland.override {withHyprtester = true;};
|
hyprland-with-tests = final.hyprland.override { withTests = true; };
|
||||||
|
|
||||||
|
hyprland-with-hyprtester = builtins.trace ''
|
||||||
|
hyprland-with-hyprtester was removed. Please use the hyprland package.
|
||||||
|
Hyprtester is always built now.
|
||||||
|
'' final.hyprland;
|
||||||
|
|
||||||
# deprecated packages
|
# deprecated packages
|
||||||
hyprland-legacy-renderer =
|
hyprland-legacy-renderer = builtins.trace ''
|
||||||
builtins.trace ''
|
|
||||||
hyprland-legacy-renderer was removed. Please use the hyprland package.
|
hyprland-legacy-renderer was removed. Please use the hyprland package.
|
||||||
Legacy renderer is no longer supported.
|
Legacy renderer is no longer supported.
|
||||||
''
|
'' final.hyprland;
|
||||||
final.hyprland;
|
|
||||||
|
|
||||||
hyprland-nvidia =
|
hyprland-nvidia = builtins.trace ''
|
||||||
builtins.trace ''
|
|
||||||
hyprland-nvidia was removed. Please use the hyprland package.
|
hyprland-nvidia was removed. Please use the hyprland package.
|
||||||
Nvidia patches are no longer needed.
|
Nvidia patches are no longer needed.
|
||||||
''
|
'' final.hyprland;
|
||||||
final.hyprland;
|
|
||||||
|
|
||||||
hyprland-hidpi =
|
hyprland-hidpi = builtins.trace ''
|
||||||
builtins.trace ''
|
|
||||||
hyprland-hidpi was removed. Please use the hyprland package.
|
hyprland-hidpi was removed. Please use the hyprland package.
|
||||||
For more information, refer to https://wiki.hypr.land/Configuring/XWayland.
|
For more information, refer to https://wiki.hypr.land/Configuring/XWayland.
|
||||||
''
|
'' final.hyprland;
|
||||||
final.hyprland;
|
}
|
||||||
})
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
|
|
@ -74,10 +86,10 @@ in {
|
||||||
# Dependencies
|
# Dependencies
|
||||||
self.overlays.hyprland-packages
|
self.overlays.hyprland-packages
|
||||||
|
|
||||||
(final: prev: {
|
(_final: prev: {
|
||||||
aquamarine = prev.aquamarine.override {debug = true;};
|
aquamarine = prev.aquamarine.override { debug = true; };
|
||||||
hyprutils = prev.hyprutils.override {debug = true;};
|
hyprutils = prev.hyprutils.override { debug = true; };
|
||||||
hyprland-debug = prev.hyprland.override {debug = true;};
|
hyprland-debug = prev.hyprland.override { debug = true; };
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -91,7 +103,8 @@ in {
|
||||||
# this version is the one used in the git submodule, and allows us to
|
# this version is the one used in the git submodule, and allows us to
|
||||||
# fetch the source without '?submodules=1'
|
# fetch the source without '?submodules=1'
|
||||||
udis86 = final: prev: {
|
udis86 = final: prev: {
|
||||||
udis86-hyprland = prev.udis86.overrideAttrs (_self: _super: {
|
udis86-hyprland = prev.udis86.overrideAttrs (
|
||||||
|
_self: _super: {
|
||||||
src = final.fetchFromGitHub {
|
src = final.fetchFromGitHub {
|
||||||
owner = "canihavesomecoffee";
|
owner = "canihavesomecoffee";
|
||||||
repo = "udis86";
|
repo = "udis86";
|
||||||
|
|
@ -99,7 +112,17 @@ in {
|
||||||
hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g=";
|
hash = "sha256-HifdUQPGsKQKQprByeIznvRLONdOXeolOsU5nkwIv3g=";
|
||||||
};
|
};
|
||||||
|
|
||||||
patches = [];
|
patches = [ ];
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
# Even though glaze itself disables it by default, nixpkgs sets ENABLE_SSL set to true.
|
||||||
|
# Since we don't include openssl, the build failes without the `enableSSL = false;` override
|
||||||
|
glaze = _final: prev: {
|
||||||
|
glaze-hyprland = prev.glaze.override {
|
||||||
|
enableSSL = false;
|
||||||
|
enableInterop = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
inputs: pkgs: let
|
inputs: pkgs:
|
||||||
|
let
|
||||||
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
flake = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
|
||||||
hyprland = flake.hyprland-with-hyprtester;
|
hyprland = flake.hyprland-with-tests;
|
||||||
in {
|
in
|
||||||
|
{
|
||||||
tests = pkgs.testers.runNixOSTest {
|
tests = pkgs.testers.runNixOSTest {
|
||||||
name = "hyprland-tests";
|
name = "hyprland-tests";
|
||||||
|
|
||||||
nodes.machine = {pkgs, ...}: {
|
nodes.machine =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
# Programs needed for tests
|
# Programs needed for tests
|
||||||
jq
|
jq
|
||||||
kitty
|
kitty
|
||||||
wl-clipboard
|
wl-clipboard
|
||||||
xorg.xeyes
|
xeyes
|
||||||
];
|
];
|
||||||
|
|
||||||
# Enabled by default for some reason
|
# Enabled by default for some reason
|
||||||
|
|
@ -27,6 +31,9 @@ in {
|
||||||
|
|
||||||
environment.etc."kitty/kitty.conf".text = ''
|
environment.etc."kitty/kitty.conf".text = ''
|
||||||
confirm_os_window_close 0
|
confirm_os_window_close 0
|
||||||
|
remember_window_size no
|
||||||
|
initial_window_width 640
|
||||||
|
initial_window_height 400
|
||||||
'';
|
'';
|
||||||
|
|
||||||
programs.hyprland = {
|
programs.hyprland = {
|
||||||
|
|
@ -60,7 +67,7 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
# Doesn't seem to do much, thought it would fix XWayland crashing
|
# Doesn't seem to do much, thought it would fix XWayland crashing
|
||||||
qemu.options = ["-vga none -device virtio-gpu-pci"];
|
qemu.options = [ "-vga none -device virtio-gpu-pci" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -68,6 +75,12 @@ in {
|
||||||
# Wait for tty to be up
|
# Wait for tty to be up
|
||||||
machine.wait_for_unit("multi-user.target")
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
|
||||||
|
# Run gtests
|
||||||
|
print("Running gtests")
|
||||||
|
exit_status, _out = machine.execute("su - alice -c 'hyprland_gtests 2>&1 | tee /tmp/gtestslog; exit ''${PIPESTATUS[0]}'")
|
||||||
|
machine.execute(f'echo {exit_status} > /tmp/exit_status_gtests')
|
||||||
|
|
||||||
# Run hyprtester testing framework/suite
|
# Run hyprtester testing framework/suite
|
||||||
print("Running hyprtester")
|
print("Running hyprtester")
|
||||||
exit_status, _out = machine.execute("su - alice -c 'hyprtester -b ${hyprland}/bin/Hyprland -c /etc/test.conf -p ${hyprland}/lib/hyprtestplugin.so 2>&1 | tee /tmp/testerlog; exit ''${PIPESTATUS[0]}'")
|
exit_status, _out = machine.execute("su - alice -c 'hyprtester -b ${hyprland}/bin/Hyprland -c /etc/test.conf -p ${hyprland}/lib/hyprtestplugin.so 2>&1 | tee /tmp/testerlog; exit ''${PIPESTATUS[0]}'")
|
||||||
|
|
@ -76,6 +89,7 @@ in {
|
||||||
# Copy logs to host
|
# Copy logs to host
|
||||||
machine.execute('cp "$(find /tmp/hypr -name *.log | head -1)" /tmp/hyprlog')
|
machine.execute('cp "$(find /tmp/hypr -name *.log | head -1)" /tmp/hyprlog')
|
||||||
machine.execute(f'echo {exit_status} > /tmp/exit_status')
|
machine.execute(f'echo {exit_status} > /tmp/exit_status')
|
||||||
|
machine.copy_from_vm("/tmp/gtestslog")
|
||||||
machine.copy_from_vm("/tmp/testerlog")
|
machine.copy_from_vm("/tmp/testerlog")
|
||||||
machine.copy_from_vm("/tmp/hyprlog")
|
machine.copy_from_vm("/tmp/hyprlog")
|
||||||
machine.copy_from_vm("/tmp/exit_status")
|
machine.copy_from_vm("/tmp/exit_status")
|
||||||
|
|
|
||||||
|
|
@ -1,366 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<protocol name="frog_color_management_v1">
|
|
||||||
|
|
||||||
<copyright>
|
|
||||||
Copyright © 2023 Joshua Ashton for Valve Software
|
|
||||||
Copyright © 2023 Xaver Hugl
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
copy of this software and associated documentation files (the "Software"),
|
|
||||||
to deal in the Software without restriction, including without limitation
|
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice (including the next
|
|
||||||
paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
</copyright>
|
|
||||||
|
|
||||||
<description summary="experimental color management protocol">
|
|
||||||
The aim of this color management extension is to get HDR games working quickly,
|
|
||||||
and have an easy way to test implementations in the wild before the upstream
|
|
||||||
protocol is ready to be merged.
|
|
||||||
For that purpose it's intentionally limited and cut down and does not serve
|
|
||||||
all uses cases.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<interface name="frog_color_management_factory_v1" version="1">
|
|
||||||
<description summary="color management factory">
|
|
||||||
The color management factory singleton creates color managed surface objects.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<request name="destroy" type="destructor"></request>
|
|
||||||
|
|
||||||
<request name="get_color_managed_surface">
|
|
||||||
<description summary="create color management interface for surface">
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<arg name="surface" type="object" interface="wl_surface"
|
|
||||||
summary="target surface" />
|
|
||||||
<arg name="callback" type="new_id" interface="frog_color_managed_surface"
|
|
||||||
summary="new color managed surface object" />
|
|
||||||
</request>
|
|
||||||
</interface>
|
|
||||||
|
|
||||||
<interface name="frog_color_managed_surface" version="1">
|
|
||||||
<description summary="color managed surface">
|
|
||||||
Interface for changing surface color management and HDR state.
|
|
||||||
|
|
||||||
An implementation must: support every part of the version
|
|
||||||
of the frog_color_managed_surface interface it exposes.
|
|
||||||
Including all known enums associated with a given version.
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<request name="destroy" type="destructor">
|
|
||||||
<description summary="destroy color managed surface">
|
|
||||||
Destroying the color managed surface resets all known color
|
|
||||||
state for the surface back to 'undefined' implementation-specific
|
|
||||||
values.
|
|
||||||
</description>
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<enum name="transfer_function">
|
|
||||||
<description summary="known transfer functions">
|
|
||||||
Extended information on the transfer functions described
|
|
||||||
here can be found in the Khronos Data Format specification:
|
|
||||||
https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html
|
|
||||||
</description>
|
|
||||||
<entry name="undefined" value="0"
|
|
||||||
summary="specifies undefined, implementation-specific handling of the surface's transfer function." />
|
|
||||||
<entry name="srgb" value="1"
|
|
||||||
summary="specifies the sRGB non-linear EOTF. An implementation may: display this as Gamma 2.2 for the purposes of being consistent with content rendering across displays, rendering_intent and user expectations." />
|
|
||||||
<entry name="gamma_22" value="2" summary="specifies gamma 2.2 power curve as the EOTF" />
|
|
||||||
<entry name="st2084_pq" value="3"
|
|
||||||
summary="specifies the SMPTE ST2084 Perceptual Quantizer (PQ) EOTF" />
|
|
||||||
<entry name="scrgb_linear" value="4"
|
|
||||||
summary="specifies the scRGB (extended sRGB) linear EOTF. Note: Primaries outside the gamut triangle specified can be expressed with negative values for this transfer function." />
|
|
||||||
</enum>
|
|
||||||
|
|
||||||
<request name="set_known_transfer_function">
|
|
||||||
<description summary="sets a known transfer function for a surface" />
|
|
||||||
<arg name="transfer_function" type="uint" enum="transfer_function"
|
|
||||||
summary="transfer function for the surface" />
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<enum name="primaries">
|
|
||||||
<description summary="known primaries" />
|
|
||||||
<entry name="undefined" value="0"
|
|
||||||
summary="specifies undefined, implementation-specific handling" />
|
|
||||||
<entry name="rec709" value="1" summary="specifies Rec.709/sRGB primaries with D65 white point" />
|
|
||||||
<entry name="rec2020" value="2"
|
|
||||||
summary="specifies Rec.2020/HDR10 primaries with D65 white point" />
|
|
||||||
</enum>
|
|
||||||
|
|
||||||
<request name="set_known_container_color_volume">
|
|
||||||
<description summary="sets the container color volume (primaries) for a surface" />
|
|
||||||
<arg name="primaries" type="uint" enum="primaries" summary="primaries for the surface" />
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<enum name="render_intent">
|
|
||||||
<description summary="known render intents">
|
|
||||||
Extended information on render intents described
|
|
||||||
here can be found in ICC.1:2022:
|
|
||||||
|
|
||||||
https://www.color.org/specification/ICC.1-2022-05.pdf
|
|
||||||
</description>
|
|
||||||
<entry name="perceptual" value="0" summary="perceptual" />
|
|
||||||
</enum>
|
|
||||||
|
|
||||||
<request name="set_render_intent">
|
|
||||||
<description summary="sets the render intent for a surface">
|
|
||||||
NOTE: On a surface with "perceptual" (default) render intent, handling of the container's
|
|
||||||
color volume
|
|
||||||
is implementation-specific, and may differ between different transfer functions it is paired
|
|
||||||
with:
|
|
||||||
ie. sRGB + 709 rendering may have it's primaries widened to more of the available display's
|
|
||||||
gamut
|
|
||||||
to be be more pleasing for the viewer.
|
|
||||||
Compared to scRGB Linear + 709 being treated faithfully as 709
|
|
||||||
(including utilizing negatives out of the 709 gamut triangle)
|
|
||||||
</description>
|
|
||||||
<arg name="render_intent" type="uint" enum="render_intent"
|
|
||||||
summary="render intent for the surface" />
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<request name="set_hdr_metadata">
|
|
||||||
<description summary="set HDR metadata for a surface">
|
|
||||||
Forwards HDR metadata from the client to the compositor.
|
|
||||||
|
|
||||||
HDR Metadata Infoframe as per CTA 861.G spec.
|
|
||||||
|
|
||||||
Usage of this HDR metadata is implementation specific and
|
|
||||||
outside of the scope of this protocol.
|
|
||||||
</description>
|
|
||||||
<arg name="mastering_display_primary_red_x" type="uint">
|
|
||||||
<description summary="red primary x coordinate">
|
|
||||||
Mastering Red Color Primary X Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="mastering_display_primary_red_y" type="uint">
|
|
||||||
<description summary="red primary y coordinate">
|
|
||||||
Mastering Red Color Primary Y Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="mastering_display_primary_green_x" type="uint">
|
|
||||||
<description summary="green primary x coordinate">
|
|
||||||
Mastering Green Color Primary X Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="mastering_display_primary_green_y" type="uint">
|
|
||||||
<description summary="green primary y coordinate">
|
|
||||||
Mastering Green Color Primary Y Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="mastering_display_primary_blue_x" type="uint">
|
|
||||||
<description summary="blue primary x coordinate">
|
|
||||||
Mastering Blue Color Primary X Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="mastering_display_primary_blue_y" type="uint">
|
|
||||||
<description summary="blue primary y coordinate">
|
|
||||||
Mastering Blue Color Primary Y Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="mastering_white_point_x" type="uint">
|
|
||||||
<description summary="white point x coordinate">
|
|
||||||
Mastering White Point X Coordinate of the Data.
|
|
||||||
|
|
||||||
These are coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="mastering_white_point_y" type="uint">
|
|
||||||
<description summary="white point y coordinate">
|
|
||||||
Mastering White Point Y Coordinate of the Data.
|
|
||||||
|
|
||||||
These are coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="max_display_mastering_luminance" type="uint">
|
|
||||||
<description summary="max display mastering luminance">
|
|
||||||
Max Mastering Display Luminance.
|
|
||||||
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
|
|
||||||
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="min_display_mastering_luminance" type="uint">
|
|
||||||
<description summary="min display mastering luminance">
|
|
||||||
Min Mastering Display Luminance.
|
|
||||||
This value is coded as an unsigned 16-bit value in units of
|
|
||||||
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
|
|
||||||
represents 6.5535 cd/m2.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="max_cll" type="uint">
|
|
||||||
<description summary="max content light level">
|
|
||||||
Max Content Light Level.
|
|
||||||
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
|
|
||||||
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="max_fall" type="uint">
|
|
||||||
<description summary="max frame average light level">
|
|
||||||
Max Frame Average Light Level.
|
|
||||||
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
|
|
||||||
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
</request>
|
|
||||||
|
|
||||||
<event name="preferred_metadata">
|
|
||||||
<description summary="preferred metadata for a surface">
|
|
||||||
Current preferred metadata for a surface.
|
|
||||||
The application should use this information to tone-map its buffers
|
|
||||||
to this target before committing.
|
|
||||||
|
|
||||||
This metadata does not necessarily correspond to any physical output, but
|
|
||||||
rather what the compositor thinks would be best for a given surface.
|
|
||||||
</description>
|
|
||||||
<arg name="transfer_function" type="uint" enum="transfer_function">
|
|
||||||
<description summary="output's current transfer function">
|
|
||||||
Specifies a known transfer function that corresponds to the
|
|
||||||
output the surface is targeting.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_display_primary_red_x" type="uint">
|
|
||||||
<description summary="red primary x coordinate">
|
|
||||||
Output Red Color Primary X Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_display_primary_red_y" type="uint">
|
|
||||||
<description summary="red primary y coordinate">
|
|
||||||
Output Red Color Primary Y Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_display_primary_green_x" type="uint">
|
|
||||||
<description summary="green primary x coordinate">
|
|
||||||
Output Green Color Primary X Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_display_primary_green_y" type="uint">
|
|
||||||
<description summary="green primary y coordinate">
|
|
||||||
Output Green Color Primary Y Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_display_primary_blue_x" type="uint">
|
|
||||||
<description summary="blue primary x coordinate">
|
|
||||||
Output Blue Color Primary X Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_display_primary_blue_y" type="uint">
|
|
||||||
<description summary="blue primary y coordinate">
|
|
||||||
Output Blue Color Primary Y Coordinate of the Data.
|
|
||||||
|
|
||||||
Coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_white_point_x" type="uint">
|
|
||||||
<description summary="white point x coordinate">
|
|
||||||
Output White Point X Coordinate of the Data.
|
|
||||||
|
|
||||||
These are coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="output_white_point_y" type="uint">
|
|
||||||
<description summary="white point y coordinate">
|
|
||||||
Output White Point Y Coordinate of the Data.
|
|
||||||
|
|
||||||
These are coded as unsigned 16-bit values in units of
|
|
||||||
0.00002, where 0x0000 represents zero and 0xC350
|
|
||||||
represents 1.0000.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="max_luminance" type="uint">
|
|
||||||
<description summary="maximum luminance">
|
|
||||||
Max Output Luminance
|
|
||||||
The max luminance in nits that the output is capable of rendering in small areas.
|
|
||||||
Content should: not exceed this value to avoid clipping.
|
|
||||||
|
|
||||||
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
|
|
||||||
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="min_luminance" type="uint">
|
|
||||||
<description summary="minimum luminance">
|
|
||||||
Min Output Luminance
|
|
||||||
The min luminance that the output is capable of rendering.
|
|
||||||
Content should: not exceed this value to avoid clipping.
|
|
||||||
|
|
||||||
This value is coded as an unsigned 16-bit value in units of
|
|
||||||
0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF
|
|
||||||
represents 6.5535 cd/m2.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
<arg name="max_full_frame_luminance" type="uint">
|
|
||||||
<description summary="maximum full frame luminance">
|
|
||||||
Max Full Frame Luminance
|
|
||||||
The max luminance in nits that the output is capable of rendering for the
|
|
||||||
full frame sustained.
|
|
||||||
|
|
||||||
This value is coded as an unsigned 16-bit value in units of 1 cd/m2,
|
|
||||||
where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2.
|
|
||||||
</description>
|
|
||||||
</arg>
|
|
||||||
</event>
|
|
||||||
</interface>
|
|
||||||
</protocol>
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
wayland_protos = dependency(
|
|
||||||
'wayland-protocols',
|
|
||||||
version: '>=1.45',
|
|
||||||
fallback: 'wayland-protocols',
|
|
||||||
default_options: ['tests=false'],
|
|
||||||
)
|
|
||||||
|
|
||||||
hyprland_protos = dependency(
|
|
||||||
'hyprland-protocols',
|
|
||||||
version: '>=0.6.4',
|
|
||||||
fallback: 'hyprland-protocols',
|
|
||||||
)
|
|
||||||
|
|
||||||
wayland_protocol_dir = wayland_protos.get_variable('pkgdatadir')
|
|
||||||
hyprland_protocol_dir = hyprland_protos.get_variable('pkgdatadir')
|
|
||||||
|
|
||||||
hyprwayland_scanner_dep = dependency('hyprwayland-scanner', version: '>=0.3.10', native: true)
|
|
||||||
hyprwayland_scanner = find_program(
|
|
||||||
hyprwayland_scanner_dep.get_variable('hyprwayland_scanner'),
|
|
||||||
native: true,
|
|
||||||
)
|
|
||||||
|
|
||||||
protocols = [
|
|
||||||
'wlr-gamma-control-unstable-v1.xml',
|
|
||||||
'wlr-foreign-toplevel-management-unstable-v1.xml',
|
|
||||||
'wlr-output-power-management-unstable-v1.xml',
|
|
||||||
'input-method-unstable-v2.xml',
|
|
||||||
'virtual-keyboard-unstable-v1.xml',
|
|
||||||
'wlr-virtual-pointer-unstable-v1.xml',
|
|
||||||
'wlr-output-management-unstable-v1.xml',
|
|
||||||
'kde-server-decoration.xml',
|
|
||||||
'wlr-layer-shell-unstable-v1.xml',
|
|
||||||
'wayland-drm.xml',
|
|
||||||
'wlr-data-control-unstable-v1.xml',
|
|
||||||
'wlr-screencopy-unstable-v1.xml',
|
|
||||||
'xx-color-management-v4.xml',
|
|
||||||
'frog-color-management-v1.xml',
|
|
||||||
hyprland_protocol_dir / 'protocols/hyprland-global-shortcuts-v1.xml',
|
|
||||||
hyprland_protocol_dir / 'protocols/hyprland-toplevel-export-v1.xml',
|
|
||||||
hyprland_protocol_dir / 'protocols/hyprland-toplevel-mapping-v1.xml',
|
|
||||||
hyprland_protocol_dir / 'protocols/hyprland-focus-grab-v1.xml',
|
|
||||||
hyprland_protocol_dir / 'protocols/hyprland-ctm-control-v1.xml',
|
|
||||||
hyprland_protocol_dir / 'protocols/hyprland-surface-v1.xml',
|
|
||||||
hyprland_protocol_dir / 'protocols/hyprland-lock-notify-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/text-input/text-input-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml',
|
|
||||||
wayland_protocol_dir / 'stable/tablet/tablet-v2.xml',
|
|
||||||
wayland_protocol_dir / 'stable/presentation-time/presentation-time.xml',
|
|
||||||
wayland_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
|
|
||||||
wayland_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
|
|
||||||
wayland_protocol_dir / 'stable/viewporter/viewporter.xml',
|
|
||||||
wayland_protocol_dir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/content-type/content-type-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/color-management/color-management-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/xdg-system-bell/xdg-system-bell-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/ext-workspace/ext-workspace-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/ext-data-control/ext-data-control-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/pointer-warp/pointer-warp-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/fifo/fifo-v1.xml',
|
|
||||||
wayland_protocol_dir / 'staging/commit-timing/commit-timing-v1.xml',
|
|
||||||
]
|
|
||||||
|
|
||||||
wl_protocols = []
|
|
||||||
foreach protocol : protocols
|
|
||||||
wl_protocols += custom_target(
|
|
||||||
protocol.underscorify(),
|
|
||||||
input: protocol,
|
|
||||||
install: true,
|
|
||||||
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
|
|
||||||
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
|
|
||||||
command: [hyprwayland_scanner, '@INPUT@', '@OUTDIR@'],
|
|
||||||
)
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
# wayland.xml generation
|
|
||||||
wayland_scanner = dependency('wayland-scanner', native: true)
|
|
||||||
wayland_scanner_datadir = wayland_scanner.get_variable('pkgdatadir')
|
|
||||||
|
|
||||||
wayland_xml = wayland_scanner_datadir / 'wayland.xml'
|
|
||||||
wayland_protocol = custom_target(
|
|
||||||
wayland_xml.underscorify(),
|
|
||||||
input: wayland_xml,
|
|
||||||
install: true,
|
|
||||||
install_dir: [false, join_paths(get_option('includedir'), 'hyprland/protocols')],
|
|
||||||
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
|
|
||||||
command: [hyprwayland_scanner, '--wayland-enums', '@INPUT@', '@OUTDIR@'],
|
|
||||||
)
|
|
||||||
|
|
||||||
lib_server_protos = static_library(
|
|
||||||
'server_protos',
|
|
||||||
wl_protocols + wayland_protocol,
|
|
||||||
)
|
|
||||||
|
|
||||||
server_protos = declare_dependency(
|
|
||||||
link_with: lib_server_protos,
|
|
||||||
sources: wl_protocols + wayland_protocol,
|
|
||||||
)
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -15,7 +15,7 @@ echo 'static const std::map<std::string, std::string> SHADERS = {' >> ./src/rend
|
||||||
for filename in `ls ${SHADERS_SRC}`; do
|
for filename in `ls ${SHADERS_SRC}`; do
|
||||||
echo "-- ${filename}"
|
echo "-- ${filename}"
|
||||||
|
|
||||||
{ echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
|
{ echo -n 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc
|
||||||
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
|
echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
|
||||||
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
|
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
|
||||||
echo "}," >> ./src/render/shaders/Shaders.hpp
|
echo "}," >> ./src/render/shaders/Shaders.hpp
|
||||||
|
|
|
||||||
1168
src/Compositor.cpp
1168
src/Compositor.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
|
#include "helpers/math/Direction.hpp"
|
||||||
#include "managers/XWaylandManager.hpp"
|
#include "managers/XWaylandManager.hpp"
|
||||||
#include "managers/KeybindManager.hpp"
|
#include "managers/KeybindManager.hpp"
|
||||||
#include "managers/SessionLockManager.hpp"
|
#include "managers/SessionLockManager.hpp"
|
||||||
#include "desktop/Window.hpp"
|
#include "desktop/view/Window.hpp"
|
||||||
#include "protocols/types/ColorManagement.hpp"
|
#include "helpers/cm/ColorManagement.hpp"
|
||||||
|
|
||||||
#include <aquamarine/backend/Backend.hpp>
|
#include <aquamarine/backend/Backend.hpp>
|
||||||
#include <aquamarine/output/Output.hpp>
|
#include <aquamarine/output/Output.hpp>
|
||||||
|
|
@ -40,6 +41,7 @@ class CCompositor {
|
||||||
} m_drmRenderNode;
|
} m_drmRenderNode;
|
||||||
|
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
|
bool m_safeMode = false;
|
||||||
SP<Aquamarine::CBackend> m_aqBackend;
|
SP<Aquamarine::CBackend> m_aqBackend;
|
||||||
|
|
||||||
std::string m_hyprTempDataRoot = "";
|
std::string m_hyprTempDataRoot = "";
|
||||||
|
|
@ -55,6 +57,7 @@ class CCompositor {
|
||||||
std::vector<PHLLS> m_layers;
|
std::vector<PHLLS> m_layers;
|
||||||
std::vector<PHLWINDOWREF> m_windowsFadingOut;
|
std::vector<PHLWINDOWREF> m_windowsFadingOut;
|
||||||
std::vector<PHLLSREF> m_surfacesFadingOut;
|
std::vector<PHLLSREF> m_surfacesFadingOut;
|
||||||
|
std::vector<SP<Desktop::View::IView>> m_otherViews;
|
||||||
|
|
||||||
std::unordered_map<std::string, MONITORID> m_monitorIDMap;
|
std::unordered_map<std::string, MONITORID> m_monitorIDMap;
|
||||||
std::unordered_map<std::string, WORKSPACEID> m_seenMonitorWorkspaceMap; // map of seen monitor names to workspace IDs
|
std::unordered_map<std::string, WORKSPACEID> m_seenMonitorWorkspaceMap; // map of seen monitor names to workspace IDs
|
||||||
|
|
@ -65,12 +68,7 @@ class CCompositor {
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void bumpNofile();
|
void bumpNofile();
|
||||||
void restoreNofile();
|
void restoreNofile();
|
||||||
|
bool setWatchdogFd(int fd);
|
||||||
WP<CWLSurfaceResource> m_lastFocus;
|
|
||||||
PHLWINDOWREF m_lastWindow;
|
|
||||||
PHLMONITORREF m_lastMonitor;
|
|
||||||
|
|
||||||
std::vector<PHLWINDOWREF> m_windowFocusHistory; // first element is the most recently focused
|
|
||||||
|
|
||||||
bool m_readyToProcess = false;
|
bool m_readyToProcess = false;
|
||||||
bool m_sessionActive = true;
|
bool m_sessionActive = true;
|
||||||
|
|
@ -99,8 +97,6 @@ class CCompositor {
|
||||||
PHLMONITOR getMonitorFromCursor();
|
PHLMONITOR getMonitorFromCursor();
|
||||||
PHLMONITOR getMonitorFromVector(const Vector2D&);
|
PHLMONITOR getMonitorFromVector(const Vector2D&);
|
||||||
void removeWindowFromVectorSafe(PHLWINDOW);
|
void removeWindowFromVectorSafe(PHLWINDOW);
|
||||||
void focusWindow(PHLWINDOW, SP<CWLSurfaceResource> pSurface = nullptr, bool preserveFocusHistory = false);
|
|
||||||
void focusSurface(SP<CWLSurfaceResource>, PHLWINDOW pWindowOwner = nullptr);
|
|
||||||
bool monitorExists(PHLMONITOR);
|
bool monitorExists(PHLMONITOR);
|
||||||
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
|
PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr);
|
||||||
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*, bool aboveLockscreen = false);
|
SP<CWLSurfaceResource> vectorToLayerSurface(const Vector2D&, std::vector<PHLLSREF>*, Vector2D*, PHLLS*, bool aboveLockscreen = false);
|
||||||
|
|
@ -118,18 +114,17 @@ class CCompositor {
|
||||||
bool isWindowActive(PHLWINDOW);
|
bool isWindowActive(PHLWINDOW);
|
||||||
void changeWindowZOrder(PHLWINDOW, bool);
|
void changeWindowZOrder(PHLWINDOW, bool);
|
||||||
void cleanupFadingOut(const MONITORID& monid);
|
void cleanupFadingOut(const MONITORID& monid);
|
||||||
PHLWINDOW getWindowInDirection(PHLWINDOW, char);
|
PHLWINDOW getWindowInDirection(PHLWINDOW, Math::eDirection);
|
||||||
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
|
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
|
||||||
PHLWINDOW getWindowCycle(PHLWINDOW cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool prev = false);
|
PHLWINDOW getWindowCycle(PHLWINDOW cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool prev = false);
|
||||||
PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool next = false);
|
PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool next = false);
|
||||||
WORKSPACEID getNextAvailableNamedWorkspace();
|
WORKSPACEID getNextAvailableNamedWorkspace();
|
||||||
bool isPointOnAnyMonitor(const Vector2D&);
|
bool isPointOnAnyMonitor(const Vector2D&);
|
||||||
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
|
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
|
||||||
CBox calculateX11WorkArea();
|
std::optional<CBox> calculateX11WorkArea();
|
||||||
PHLMONITOR getMonitorInDirection(const char&);
|
PHLMONITOR getMonitorInDirection(Math::eDirection);
|
||||||
PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&);
|
PHLMONITOR getMonitorInDirection(PHLMONITOR, Math::eDirection);
|
||||||
void updateAllWindowsAnimatedDecorationValues();
|
void updateAllWindowsAnimatedDecorationValues();
|
||||||
void updateWindowAnimatedDecorationValues(PHLWINDOW);
|
|
||||||
MONITORID getNextAvailableMonitorID(std::string const& name);
|
MONITORID getNextAvailableMonitorID(std::string const& name);
|
||||||
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
|
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
|
||||||
void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR);
|
void swapActiveWorkspaces(PHLMONITOR, PHLMONITOR);
|
||||||
|
|
@ -137,7 +132,7 @@ class CCompositor {
|
||||||
bool workspaceIDOutOfBounds(const WORKSPACEID&);
|
bool workspaceIDOutOfBounds(const WORKSPACEID&);
|
||||||
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
void setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||||
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
void setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE);
|
||||||
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const SFullscreenState state);
|
void setWindowFullscreenState(const PHLWINDOW PWINDOW, const Desktop::View::SFullscreenState state);
|
||||||
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
|
void changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON);
|
||||||
PHLWINDOW getX11Parent(PHLWINDOW);
|
PHLWINDOW getX11Parent(PHLWINDOW);
|
||||||
void scheduleFrameForMonitor(PHLMONITOR, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
|
void scheduleFrameForMonitor(PHLMONITOR, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
|
||||||
|
|
@ -151,7 +146,6 @@ class CCompositor {
|
||||||
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
|
Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&);
|
||||||
[[nodiscard]] PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, const std::string& name = "",
|
[[nodiscard]] PHLWORKSPACE createNewWorkspace(const WORKSPACEID&, const MONITORID&, const std::string& name = "",
|
||||||
bool isEmpty = true); // will be deleted next frame if left empty and unfocused!
|
bool isEmpty = true); // will be deleted next frame if left empty and unfocused!
|
||||||
void setActiveMonitor(PHLMONITOR);
|
|
||||||
bool isWorkspaceSpecial(const WORKSPACEID&);
|
bool isWorkspaceSpecial(const WORKSPACEID&);
|
||||||
WORKSPACEID getNewSpecialID();
|
WORKSPACEID getNewSpecialID();
|
||||||
void performUserChecks();
|
void performUserChecks();
|
||||||
|
|
@ -167,9 +161,12 @@ class CCompositor {
|
||||||
void updateSuspendedStates();
|
void updateSuspendedStates();
|
||||||
void onNewMonitor(SP<Aquamarine::IOutput> output);
|
void onNewMonitor(SP<Aquamarine::IOutput> output);
|
||||||
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace = nullptr);
|
void ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace = nullptr);
|
||||||
|
void ensureWorkspacesOnAssignedMonitors();
|
||||||
std::optional<unsigned int> getVTNr();
|
std::optional<unsigned int> getVTNr();
|
||||||
|
bool isVRRActiveOnAnyMonitor() const;
|
||||||
|
|
||||||
NColorManagement::SImageDescription getPreferredImageDescription();
|
NColorManagement::PImageDescription getPreferredImageDescription();
|
||||||
|
NColorManagement::PImageDescription getHDRImageDescription();
|
||||||
bool shouldChangePreferredImageDescription();
|
bool shouldChangePreferredImageDescription();
|
||||||
|
|
||||||
bool supportsDrmSyncobjTimeline() const;
|
bool supportsDrmSyncobjTimeline() const;
|
||||||
|
|
@ -185,10 +182,12 @@ class CCompositor {
|
||||||
void createLockFile();
|
void createLockFile();
|
||||||
void removeLockFile();
|
void removeLockFile();
|
||||||
void setMallocThreshold();
|
void setMallocThreshold();
|
||||||
|
void openSafeModeBox();
|
||||||
|
|
||||||
uint64_t m_hyprlandPID = 0;
|
uint64_t m_hyprlandPID = 0;
|
||||||
wl_event_source* m_critSigSource = nullptr;
|
wl_event_source* m_critSigSource = nullptr;
|
||||||
rlimit m_originalNofile = {};
|
rlimit m_originalNofile = {};
|
||||||
|
Hyprutils::OS::CFileDescriptor m_watchdogWriteFd;
|
||||||
|
|
||||||
std::vector<PHLWORKSPACEREF> m_workspaces;
|
std::vector<PHLWORKSPACEREF> m_workspaces;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,6 @@ enum eInputType : uint8_t {
|
||||||
INPUT_TYPE_MOTION
|
INPUT_TYPE_MOTION
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SCallbackInfo {
|
|
||||||
bool cancelled = false; /* on cancellable events, will cancel the event. */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum eHyprCtlOutputFormat : uint8_t {
|
enum eHyprCtlOutputFormat : uint8_t {
|
||||||
FORMAT_NORMAL = 0,
|
FORMAT_NORMAL = 0,
|
||||||
FORMAT_JSON
|
FORMAT_JSON
|
||||||
|
|
@ -62,5 +58,3 @@ struct SDispatchResult {
|
||||||
using WINDOWID = int64_t;
|
using WINDOWID = int64_t;
|
||||||
using MONITORID = int64_t;
|
using MONITORID = int64_t;
|
||||||
using WORKSPACEID = int64_t;
|
using WORKSPACEID = int64_t;
|
||||||
|
|
||||||
using HOOK_CALLBACK_FN = std::function<void(void*, SCallbackInfo&, std::any)>;
|
|
||||||
|
|
|
||||||
|
|
@ -97,27 +97,29 @@ class CCssGapData : public ICustomConfigValueData {
|
||||||
int64_t m_bottom;
|
int64_t m_bottom;
|
||||||
int64_t m_left;
|
int64_t m_left;
|
||||||
|
|
||||||
void parseGapData(CVarList varlist) {
|
void parseGapData(CVarList2 varlist) {
|
||||||
|
const auto toInt = [](std::string_view string) -> int { return std::stoi(std::string(string)); };
|
||||||
|
|
||||||
switch (varlist.size()) {
|
switch (varlist.size()) {
|
||||||
case 1: {
|
case 1: {
|
||||||
*this = CCssGapData(std::stoi(varlist[0]));
|
*this = CCssGapData(toInt(varlist[0]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]));
|
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 3: {
|
case 3: {
|
||||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]));
|
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3]));
|
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]), toInt(varlist[3]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
Debug::log(WARN, "Too many arguments provided for gaps.");
|
Log::logger->log(Log::WARN, "Too many arguments provided for gaps.");
|
||||||
*this = CCssGapData(std::stoi(varlist[0]), std::stoi(varlist[1]), std::stoi(varlist[2]), std::stoi(varlist[3]));
|
*this = CCssGapData(toInt(varlist[0]), toInt(varlist[1]), toInt(varlist[2]), toInt(varlist[3]));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_INT,
|
.type = CONFIG_OPTION_INT,
|
||||||
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
|
.data = SConfigOptionDescription::SRangeData{1, 0, 20},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "general:no_border_on_floating",
|
|
||||||
.description = "disable borders for floating windows",
|
|
||||||
.type = CONFIG_OPTION_BOOL,
|
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
|
||||||
},
|
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "general:gaps_in",
|
.value = "general:gaps_in",
|
||||||
.description = "gaps between windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20)",
|
.description = "gaps between windows\n\nsupports css style gaps (top, right, bottom, left -> 5 10 15 20)",
|
||||||
|
|
@ -144,10 +138,16 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "general:modal_parent_blocking",
|
.value = "general:modal_parent_blocking",
|
||||||
.description = "If true, parent windows of modals will not be interactive.",
|
.description = "if true, parent windows of modals will not be interactive.",
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{true},
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "general:locale",
|
||||||
|
.description = "overrides the system locale",
|
||||||
|
.type = CONFIG_OPTION_STRING_SHORT,
|
||||||
|
.data = SConfigOptionDescription::SStringData{""},
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* decoration:
|
* decoration:
|
||||||
|
|
@ -1115,6 +1115,18 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SRangeData{0, -20, 20},
|
.data = SConfigOptionDescription::SRangeData{0, -20, 20},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "group:groupbar:text_padding",
|
||||||
|
.description = "set horizontal padding for a text",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SRangeData{0, 0, 22},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "group:groupbar:blur",
|
||||||
|
.description = "enable background blur for groupbars",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* misc:
|
* misc:
|
||||||
|
|
@ -1273,11 +1285,11 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.data = SConfigOptionDescription::SBoolData{true},
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "misc:new_window_takes_over_fullscreen",
|
.value = "misc:on_focus_under_fullscreen",
|
||||||
.description = "if there is a fullscreen or maximized window, decide whether a new tiled window opened should replace it, stay behind or disable the fullscreen/maximized "
|
.description = "if there is a fullscreen or maximized window, decide whether a tiled window requested to focus should replace it, stay behind or disable the "
|
||||||
"state. 0 - behind, 1 - takes over, 2 - unfullscreen/unmaxize [0/1/2]",
|
"fullscreen/maximized state. 0 - ignore focus request (keep focus on fullscreen window), 1 - takes over, 2 - unfullscreen/unmaximize [0/1/2]",
|
||||||
.type = CONFIG_OPTION_INT,
|
.type = CONFIG_OPTION_INT,
|
||||||
.data = SConfigOptionDescription::SRangeData{0, 0, 2},
|
.data = SConfigOptionDescription::SRangeData{2, 0, 2},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "misc:exit_window_retains_fullscreen",
|
.value = "misc:exit_window_retains_fullscreen",
|
||||||
|
|
@ -1315,6 +1327,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "misc:disable_watchdog_warning",
|
||||||
|
.description = "whether to disable the warning about not using start-hyprland.",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "misc:lockdead_screen_delay",
|
.value = "misc:lockdead_screen_delay",
|
||||||
.description = "the delay in ms after the lockdead screen appears if the lock screen did not appear after a lock event occurred.",
|
.description = "the delay in ms after the lockdead screen appears if the lock screen did not appear after a lock event occurred.",
|
||||||
|
|
@ -1550,10 +1568,28 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "render:cm_sdr_eotf",
|
.value = "render:cm_sdr_eotf",
|
||||||
.description = "Default transfer function for displaying SDR apps. 0 - Treat unspecified as sRGB, 1 - Treat unspecified as Gamma 2.2, 2 - Treat "
|
.description = "Default transfer function for displaying SDR apps. default - Use default value (Gamma 2.2), gamma22 - Treat unspecified as Gamma 2.2, gamma22force - Treat "
|
||||||
"unspecified and sRGB as Gamma 2.2",
|
"unspecified and sRGB as Gamma 2.2, srgb - Treat unspecified as sRGB",
|
||||||
.type = CONFIG_OPTION_CHOICE,
|
.type = CONFIG_OPTION_STRING_SHORT,
|
||||||
.data = SConfigOptionDescription::SChoiceData{0, "srgb,gamma22,gamma22force"},
|
.data = SConfigOptionDescription::SStringData{"default"},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "render:commit_timing_enabled",
|
||||||
|
.description = "Enable commit timing proto. Requires restart",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "render:icc_vcgt_enabled",
|
||||||
|
.description = "Enable sending VCGT ramps to KMS with ICC profiles",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.value = "render:use_shader_blur_blend",
|
||||||
|
.description = "Use experimental blurred bg blending",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1646,6 +1682,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "cursor:zoom_detached_camera",
|
||||||
|
.description = "Detaches the camera from the mouse when zoomed in, only ever moving to keep the mouse in view",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "cursor:enable_hyprcursor",
|
.value = "cursor:enable_hyprcursor",
|
||||||
.description = "whether to enable hyprcursor support",
|
.description = "whether to enable hyprcursor support",
|
||||||
|
|
@ -1665,10 +1707,16 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.data = SConfigOptionDescription::SBoolData{true},
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "cursor:use_cpu_buffer",
|
.value = "cursor:hide_on_tablet",
|
||||||
.description = "Makes HW cursors use a CPU buffer. Required on Nvidia to have HW cursors. Experimental",
|
.description = "Hides the cursor when the last input was a tablet input until a mouse input is done.",
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "cursor:use_cpu_buffer",
|
||||||
|
.description = "Makes HW cursors use a CPU buffer. Required on Nvidia to have HW cursors. 0 - off, 1 - on, 2 - auto (nvidia only)",
|
||||||
|
.type = CONFIG_OPTION_INT,
|
||||||
|
.data = SConfigOptionDescription::SRangeData{.value = 2, .min = 0, .max = 2},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "cursor:sync_gsettings_theme",
|
.value = "cursor:sync_gsettings_theme",
|
||||||
|
|
@ -1722,6 +1770,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "debug:gl_debugging",
|
||||||
|
.description = "enable OpenGL debugging and error checking, they hurt performance.",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
|
},
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "debug:disable_logs",
|
.value = "debug:disable_logs",
|
||||||
.description = "disable logging to a file",
|
.description = "disable logging to a file",
|
||||||
|
|
@ -1800,6 +1854,46 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "debug:ds_handle_same_buffer",
|
||||||
|
.description = "Special case for DS with unmodified buffer",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "debug:ds_handle_same_buffer_fifo",
|
||||||
|
.description = "Special case for DS with unmodified buffer unlocks fifo",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "debug:fifo_pending_workaround",
|
||||||
|
.description = "Fifo workaround for empty pending list",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "debug:render_solitary_wo_damage",
|
||||||
|
.description = "Render solitary window with empty damage",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* layout:
|
||||||
|
*/
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "layout:single_window_aspect_ratio",
|
||||||
|
.description = "If specified, whenever only a single window is open, it will be coerced into the specified aspect ratio. Ignored if the y-value is zero.",
|
||||||
|
.type = CONFIG_OPTION_VECTOR,
|
||||||
|
.data = SConfigOptionDescription::SVectorData{{0, 0}, {0, 0}, {1000., 1000.}},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "layout:single_window_aspect_ratio_tolerance",
|
||||||
|
.description = "Minimum distance for single_window_aspect_ratio to take effect, in fractions of the monitor's size.",
|
||||||
|
.type = CONFIG_OPTION_FLOAT,
|
||||||
|
.data = SConfigOptionDescription::SFloatData{0.1f, 0.f, 1.f},
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dwindle:
|
* dwindle:
|
||||||
|
|
@ -1880,18 +1974,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{false},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "dwindle:single_window_aspect_ratio",
|
|
||||||
.description = "If specified, whenever only a single window is open, it will be coerced into the specified aspect ratio. Ignored if the y-value is zero.",
|
|
||||||
.type = CONFIG_OPTION_VECTOR,
|
|
||||||
.data = SConfigOptionDescription::SVectorData{{0, 0}, {0, 0}, {1000., 1000.}},
|
|
||||||
},
|
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "dwindle:single_window_aspect_ratio_tolerance",
|
|
||||||
.description = "Minimum distance for single_window_aspect_ratio to take effect, in fractions of the monitor's size.",
|
|
||||||
.type = CONFIG_OPTION_FLOAT,
|
|
||||||
.data = SConfigOptionDescription::SFloatData{0.1f, 0.f, 1.f},
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* master:
|
* master:
|
||||||
|
|
@ -1940,12 +2022,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
.type = CONFIG_OPTION_STRING_SHORT,
|
.type = CONFIG_OPTION_STRING_SHORT,
|
||||||
.data = SConfigOptionDescription::SStringData{"left"},
|
.data = SConfigOptionDescription::SStringData{"left"},
|
||||||
},
|
},
|
||||||
SConfigOptionDescription{
|
|
||||||
.value = "master:inherit_fullscreen",
|
|
||||||
.description = "inherit fullscreen status when cycling/swapping to another window (e.g. monocle layout)",
|
|
||||||
.type = CONFIG_OPTION_BOOL,
|
|
||||||
.data = SConfigOptionDescription::SBoolData{true},
|
|
||||||
},
|
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "master:slave_count_for_center_master",
|
.value = "master:slave_count_for_center_master",
|
||||||
.description = "when using orientation=center, make the master window centered only when at least this many slave windows are open. (Set 0 to always_center_master)",
|
.description = "when using orientation=center, make the master window centered only when at least this many slave windows are open. (Set 0 to always_center_master)",
|
||||||
|
|
@ -1984,13 +2060,79 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Experimental
|
* scrolling:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SConfigOptionDescription{
|
SConfigOptionDescription{
|
||||||
.value = "experimental:xx_color_management_v4",
|
.value = "scrolling:fullscreen_on_one_column",
|
||||||
.description = "enable color management protocol",
|
.description = "when enabled, a single column on a workspace will always span the entire screen.",
|
||||||
.type = CONFIG_OPTION_BOOL,
|
.type = CONFIG_OPTION_BOOL,
|
||||||
.data = SConfigOptionDescription::SBoolData{false},
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
},
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:column_width",
|
||||||
|
.description = "the default width of a column, [0.1 - 1.0].",
|
||||||
|
.type = CONFIG_OPTION_FLOAT,
|
||||||
|
.data = SConfigOptionDescription::SFloatData{.value = 0.5, .min = 0.1, .max = 1.0},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:focus_fit_method",
|
||||||
|
.description = "When a column is focused, what method should be used to bring it into view",
|
||||||
|
.type = CONFIG_OPTION_CHOICE,
|
||||||
|
.data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "center,fit"},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:follow_focus",
|
||||||
|
.description = "when a window is focused, should the layout move to bring it into view automatically",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{.value = true},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:follow_min_visible",
|
||||||
|
.description = "when a window is focused, require that at least a given fraction of it is visible for focus to follow",
|
||||||
|
.type = CONFIG_OPTION_FLOAT,
|
||||||
|
.data = SConfigOptionDescription::SFloatData{.value = 0.4, .min = 0.0, .max = 1.0},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:explicit_column_widths",
|
||||||
|
.description = "A comma-separated list of preconfigured widths for colresize +conf/-conf",
|
||||||
|
.type = CONFIG_OPTION_STRING_SHORT,
|
||||||
|
.data = SConfigOptionDescription::SStringData{"0.333, 0.5, 0.667, 1.0"},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:direction",
|
||||||
|
.description = "Direction in which new windows appear and the layout scrolls",
|
||||||
|
.type = CONFIG_OPTION_CHOICE,
|
||||||
|
.data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "right,left,down,up"},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:wrap_focus",
|
||||||
|
.description = "Determines if column focus wraps around when going before the first column or past the last column",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{.value = true},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "scrolling:wrap_swapcol",
|
||||||
|
.description = "Determines if column movement wraps around when moving to before the first column or past the last column",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{.value = true},
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quirks
|
||||||
|
*/
|
||||||
|
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "quirks:prefer_hdr",
|
||||||
|
.description = "Prefer HDR mode. 0 - off, 1 - always, 2 - gamescope only",
|
||||||
|
.type = CONFIG_OPTION_INT,
|
||||||
|
.data = SConfigOptionDescription::SRangeData{.value = 0, .min = 0, .max = 2},
|
||||||
|
},
|
||||||
|
SConfigOptionDescription{
|
||||||
|
.value = "quirks:skip_non_kms_dmabuf_formats",
|
||||||
|
.description = "Do not report dmabuf formats which cannot be imported into KMS",
|
||||||
|
.type = CONFIG_OPTION_BOOL,
|
||||||
|
.data = SConfigOptionDescription::SBoolData{true},
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -12,15 +12,14 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
#include "../helpers/Monitor.hpp"
|
#include "../helpers/Monitor.hpp"
|
||||||
#include "../desktop/Window.hpp"
|
#include "../desktop/view/Window.hpp"
|
||||||
#include "../desktop/LayerRule.hpp"
|
|
||||||
|
|
||||||
#include "ConfigDataValues.hpp"
|
#include "ConfigDataValues.hpp"
|
||||||
#include "../SharedDefs.hpp"
|
#include "../SharedDefs.hpp"
|
||||||
#include "../helpers/Color.hpp"
|
#include "../helpers/Color.hpp"
|
||||||
#include "../desktop/DesktopTypes.hpp"
|
#include "../desktop/DesktopTypes.hpp"
|
||||||
|
#include "../desktop/reserved/ReservedArea.hpp"
|
||||||
#include "../helpers/memory/Memory.hpp"
|
#include "../helpers/memory/Memory.hpp"
|
||||||
#include "../desktop/WindowRule.hpp"
|
|
||||||
#include "../managers/XWaylandManager.hpp"
|
#include "../managers/XWaylandManager.hpp"
|
||||||
#include "../managers/KeybindManager.hpp"
|
#include "../managers/KeybindManager.hpp"
|
||||||
|
|
||||||
|
|
@ -47,16 +46,10 @@ struct SWorkspaceRule {
|
||||||
std::optional<bool> noShadow;
|
std::optional<bool> noShadow;
|
||||||
std::optional<std::string> onCreatedEmptyRunCmd;
|
std::optional<std::string> onCreatedEmptyRunCmd;
|
||||||
std::optional<std::string> defaultName;
|
std::optional<std::string> defaultName;
|
||||||
|
std::optional<std::string> layout;
|
||||||
std::map<std::string, std::string> layoutopts;
|
std::map<std::string, std::string> layoutopts;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SMonitorAdditionalReservedArea {
|
|
||||||
int top = 0;
|
|
||||||
int bottom = 0;
|
|
||||||
int left = 0;
|
|
||||||
int right = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SPluginKeyword {
|
struct SPluginKeyword {
|
||||||
HANDLE handle = nullptr;
|
HANDLE handle = nullptr;
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
|
|
@ -68,11 +61,6 @@ struct SPluginVariable {
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SExecRequestedRule {
|
|
||||||
std::string szRule = "";
|
|
||||||
uint64_t iPid = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum eConfigOptionType : uint8_t {
|
enum eConfigOptionType : uint8_t {
|
||||||
CONFIG_OPTION_BOOL = 0,
|
CONFIG_OPTION_BOOL = 0,
|
||||||
CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/
|
CONFIG_OPTION_INT = 1, /* e.g. 0/1/2*/
|
||||||
|
|
@ -189,10 +177,11 @@ class CMonitorRuleParser {
|
||||||
bool parseSDRBrightness(const std::string& value);
|
bool parseSDRBrightness(const std::string& value);
|
||||||
bool parseSDRSaturation(const std::string& value);
|
bool parseSDRSaturation(const std::string& value);
|
||||||
bool parseVRR(const std::string& value);
|
bool parseVRR(const std::string& value);
|
||||||
|
bool parseICC(const std::string& value);
|
||||||
|
|
||||||
void setDisabled();
|
void setDisabled();
|
||||||
void setMirror(const std::string& value);
|
void setMirror(const std::string& value);
|
||||||
bool setReserved(const SMonitorAdditionalReservedArea& value);
|
bool setReserved(const Desktop::CReservedArea& value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SMonitorRule m_rule;
|
SMonitorRule m_rule;
|
||||||
|
|
@ -214,7 +203,6 @@ class CConfigManager {
|
||||||
bool deviceConfigExplicitlySet(const std::string&, const std::string&);
|
bool deviceConfigExplicitlySet(const std::string&, const std::string&);
|
||||||
bool deviceConfigExists(const std::string&);
|
bool deviceConfigExists(const std::string&);
|
||||||
Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback);
|
Hyprlang::CConfigValue* getConfigValueSafeDevice(const std::string& dev, const std::string& val, const std::string& fallback);
|
||||||
bool shouldBlurLS(const std::string&);
|
|
||||||
|
|
||||||
void* const* getConfigValuePtr(const std::string&);
|
void* const* getConfigValuePtr(const std::string&);
|
||||||
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
|
Hyprlang::CConfigValue* getHyprlangConfigValuePtr(const std::string& name, const std::string& specialCat = "");
|
||||||
|
|
@ -229,14 +217,10 @@ class CConfigManager {
|
||||||
std::string getBoundMonitorStringForWS(const std::string&);
|
std::string getBoundMonitorStringForWS(const std::string&);
|
||||||
const std::vector<SWorkspaceRule>& getAllWorkspaceRules();
|
const std::vector<SWorkspaceRule>& getAllWorkspaceRules();
|
||||||
|
|
||||||
std::vector<SP<CWindowRule>> getMatchingRules(PHLWINDOW, bool dynamic = true, bool shadowExec = false);
|
|
||||||
std::vector<SP<CLayerRule>> getMatchingRules(PHLLS);
|
|
||||||
void ensurePersistentWorkspacesPresent();
|
void ensurePersistentWorkspacesPresent();
|
||||||
|
|
||||||
const std::vector<SConfigOptionDescription>& getAllDescriptions();
|
const std::vector<SConfigOptionDescription>& getAllDescriptions();
|
||||||
|
|
||||||
std::unordered_map<std::string, SMonitorAdditionalReservedArea> m_mAdditionalReservedAreas;
|
|
||||||
|
|
||||||
const std::unordered_map<std::string, SP<Hyprutils::Animation::SAnimationPropertyConfig>>& getAnimationConfig();
|
const std::unordered_map<std::string, SP<Hyprutils::Animation::SAnimationPropertyConfig>>& getAnimationConfig();
|
||||||
|
|
||||||
void addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value);
|
void addPluginConfigVar(HANDLE handle, const std::string& name, const Hyprlang::CConfigValue& value);
|
||||||
|
|
@ -260,8 +244,6 @@ class CConfigManager {
|
||||||
|
|
||||||
SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&);
|
SP<Hyprutils::Animation::SAnimationPropertyConfig> getAnimationPropertyConfig(const std::string&);
|
||||||
|
|
||||||
void addExecRule(const SExecRequestedRule&);
|
|
||||||
|
|
||||||
void handlePluginLoads();
|
void handlePluginLoads();
|
||||||
std::string getErrors();
|
std::string getErrors();
|
||||||
|
|
||||||
|
|
@ -274,22 +256,24 @@ class CConfigManager {
|
||||||
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
|
std::optional<std::string> handleMonitor(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleBind(const std::string&, const std::string&);
|
std::optional<std::string> handleBind(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
|
std::optional<std::string> handleUnbind(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleWindowRule(const std::string&, const std::string&);
|
|
||||||
std::optional<std::string> handleLayerRule(const std::string&, const std::string&);
|
|
||||||
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
|
std::optional<std::string> handleWorkspaceRules(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleBezier(const std::string&, const std::string&);
|
std::optional<std::string> handleBezier(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
|
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleSource(const std::string&, const std::string&);
|
std::optional<std::string> handleSource(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
|
std::optional<std::string> handleSubmap(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleBlurLS(const std::string&, const std::string&);
|
|
||||||
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
|
std::optional<std::string> handleBindWS(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleEnv(const std::string&, const std::string&);
|
std::optional<std::string> handleEnv(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
|
std::optional<std::string> handlePlugin(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handlePermission(const std::string&, const std::string&);
|
std::optional<std::string> handlePermission(const std::string&, const std::string&);
|
||||||
std::optional<std::string> handleGesture(const std::string&, const std::string&);
|
std::optional<std::string> handleGesture(const std::string&, const std::string&);
|
||||||
|
std::optional<std::string> handleWindowrule(const std::string&, const std::string&);
|
||||||
|
std::optional<std::string> handleLayerrule(const std::string&, const std::string&);
|
||||||
|
|
||||||
std::optional<std::string> handleMonitorv2(const std::string& output);
|
std::optional<std::string> handleMonitorv2(const std::string& output);
|
||||||
Hyprlang::CParseResult handleMonitorv2();
|
Hyprlang::CParseResult handleMonitorv2();
|
||||||
|
std::optional<std::string> addRuleFromConfigKey(const std::string& name);
|
||||||
|
std::optional<std::string> addLayerRuleFromConfigKey(const std::string& name);
|
||||||
|
Hyprlang::CParseResult reloadRules();
|
||||||
|
|
||||||
std::string m_configCurrentPath;
|
std::string m_configCurrentPath;
|
||||||
|
|
||||||
|
|
@ -310,19 +294,16 @@ class CConfigManager {
|
||||||
|
|
||||||
SSubmap m_currentSubmap;
|
SSubmap m_currentSubmap;
|
||||||
|
|
||||||
std::vector<SExecRequestedRule> m_execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
|
|
||||||
|
|
||||||
std::vector<std::string> m_declaredPlugins;
|
std::vector<std::string> m_declaredPlugins;
|
||||||
std::vector<SPluginKeyword> m_pluginKeywords;
|
std::vector<SPluginKeyword> m_pluginKeywords;
|
||||||
std::vector<SPluginVariable> m_pluginVariables;
|
std::vector<SPluginVariable> m_pluginVariables;
|
||||||
|
|
||||||
|
std::vector<SP<Desktop::Rule::IRule>> m_keywordRules;
|
||||||
|
|
||||||
bool m_isFirstLaunch = true; // For exec-once
|
bool m_isFirstLaunch = true; // For exec-once
|
||||||
|
|
||||||
std::vector<SMonitorRule> m_monitorRules;
|
std::vector<SMonitorRule> m_monitorRules;
|
||||||
std::vector<SWorkspaceRule> m_workspaceRules;
|
std::vector<SWorkspaceRule> m_workspaceRules;
|
||||||
std::vector<SP<CWindowRule>> m_windowRules;
|
|
||||||
std::vector<SP<CLayerRule>> m_layerRules;
|
|
||||||
std::vector<std::string> m_blurLSNamespaces;
|
|
||||||
|
|
||||||
bool m_firstExecDispatched = false;
|
bool m_firstExecDispatched = false;
|
||||||
bool m_manualCrashInitiated = false;
|
bool m_manualCrashInitiated = false;
|
||||||
|
|
@ -336,11 +317,11 @@ class CConfigManager {
|
||||||
uint32_t m_configValueNumber = 0;
|
uint32_t m_configValueNumber = 0;
|
||||||
|
|
||||||
// internal methods
|
// internal methods
|
||||||
void updateBlurredLS(const std::string&, const bool);
|
|
||||||
void setDefaultAnimationVars();
|
void setDefaultAnimationVars();
|
||||||
std::optional<std::string> resetHLConfig();
|
std::optional<std::string> resetHLConfig();
|
||||||
std::optional<std::string> generateConfig(std::string configPath);
|
std::optional<std::string> generateConfig(std::string configPath, bool safeMode = false);
|
||||||
std::optional<std::string> verifyConfigExists();
|
std::optional<std::string> verifyConfigExists();
|
||||||
|
void reloadRuleConfigs();
|
||||||
|
|
||||||
void postConfigReload(const Hyprlang::CParseResult& result);
|
void postConfigReload(const Hyprlang::CParseResult& result);
|
||||||
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
|
SWorkspaceRule mergeWorkspaceRules(const SWorkspaceRule&, const SWorkspaceRule&);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#include "ConfigWatcher.hpp"
|
#include "ConfigWatcher.hpp"
|
||||||
|
#if defined(__linux__)
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
|
#endif
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include "../debug/Log.hpp"
|
#include "../debug/log/Logger.hpp"
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -11,14 +13,14 @@ using namespace Hyprutils::OS;
|
||||||
|
|
||||||
CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) {
|
CConfigWatcher::CConfigWatcher() : m_inotifyFd(inotify_init()) {
|
||||||
if (!m_inotifyFd.isValid()) {
|
if (!m_inotifyFd.isValid()) {
|
||||||
Debug::log(ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
|
Log::logger->log(Log::ERR, "CConfigWatcher couldn't open an inotify node. Config will not be automatically reloaded");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make CFileDescriptor take F_GETFL, F_SETFL
|
// TODO: make CFileDescriptor take F_GETFL, F_SETFL
|
||||||
const int FLAGS = fcntl(m_inotifyFd.get(), F_GETFL, 0);
|
const int FLAGS = fcntl(m_inotifyFd.get(), F_GETFL, 0);
|
||||||
if (fcntl(m_inotifyFd.get(), F_SETFL, FLAGS | O_NONBLOCK) < 0) {
|
if (fcntl(m_inotifyFd.get(), F_SETFL, FLAGS | O_NONBLOCK) < 0) {
|
||||||
Debug::log(ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
|
Log::logger->log(Log::ERR, "CConfigWatcher couldn't non-block inotify node. Config will not be automatically reloaded");
|
||||||
m_inotifyFd.reset();
|
m_inotifyFd.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -76,19 +78,19 @@ void CConfigWatcher::onInotifyEvent() {
|
||||||
const auto* ev = rc<const inotify_event*>(buffer.data() + offset);
|
const auto* ev = rc<const inotify_event*>(buffer.data() + offset);
|
||||||
|
|
||||||
if (offset + sizeof(inotify_event) > sc<size_t>(bytesRead)) {
|
if (offset + sizeof(inotify_event) > sc<size_t>(bytesRead)) {
|
||||||
Debug::log(ERR, "CConfigWatcher: malformed inotify event, truncated header");
|
Log::logger->log(Log::ERR, "CConfigWatcher: malformed inotify event, truncated header");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset + sizeof(inotify_event) + ev->len > sc<size_t>(bytesRead)) {
|
if (offset + sizeof(inotify_event) + ev->len > sc<size_t>(bytesRead)) {
|
||||||
Debug::log(ERR, "CConfigWatcher: malformed inotify event, truncated name field");
|
Log::logger->log(Log::ERR, "CConfigWatcher: malformed inotify event, truncated name field");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto WD = std::ranges::find_if(m_watches, [wd = ev->wd](const auto& e) { return e.wd == wd; });
|
const auto WD = std::ranges::find_if(m_watches, [wd = ev->wd](const auto& e) { return e.wd == wd; });
|
||||||
|
|
||||||
if (WD == m_watches.end())
|
if (WD == m_watches.end())
|
||||||
Debug::log(ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev->wd);
|
Log::logger->log(Log::ERR, "CConfigWatcher: got an event for wd {} which we don't have?!", ev->wd);
|
||||||
else
|
else
|
||||||
m_watchCallback(SConfigWatchEvent{
|
m_watchCallback(SConfigWatchEvent{
|
||||||
.file = WD->file,
|
.file = WD->file,
|
||||||
|
|
|
||||||
|
|
@ -40,21 +40,28 @@ using namespace Hyprutils::OS;
|
||||||
#include "../devices/ITouch.hpp"
|
#include "../devices/ITouch.hpp"
|
||||||
#include "../devices/Tablet.hpp"
|
#include "../devices/Tablet.hpp"
|
||||||
#include "../protocols/GlobalShortcuts.hpp"
|
#include "../protocols/GlobalShortcuts.hpp"
|
||||||
#include "debug/RollingLogFollow.hpp"
|
#include "debug/log/RollingLogFollow.hpp"
|
||||||
#include "config/ConfigManager.hpp"
|
#include "config/ConfigManager.hpp"
|
||||||
#include "helpers/MiscFunctions.hpp"
|
#include "helpers/MiscFunctions.hpp"
|
||||||
#include "../desktop/LayerSurface.hpp"
|
#include "../desktop/view/LayerSurface.hpp"
|
||||||
|
#include "../desktop/view/Group.hpp"
|
||||||
|
#include "../desktop/rule/Engine.hpp"
|
||||||
|
#include "../desktop/history/WindowHistoryTracker.hpp"
|
||||||
|
#include "../desktop/state/FocusState.hpp"
|
||||||
#include "../version.h"
|
#include "../version.h"
|
||||||
|
|
||||||
#include "../Compositor.hpp"
|
#include "../Compositor.hpp"
|
||||||
#include "../managers/input/InputManager.hpp"
|
#include "../managers/input/InputManager.hpp"
|
||||||
#include "../managers/XWaylandManager.hpp"
|
#include "../managers/XWaylandManager.hpp"
|
||||||
#include "../managers/LayoutManager.hpp"
|
|
||||||
#include "../plugins/PluginSystem.hpp"
|
#include "../plugins/PluginSystem.hpp"
|
||||||
#include "../managers/animation/AnimationManager.hpp"
|
#include "../managers/animation/AnimationManager.hpp"
|
||||||
#include "../debug/HyprNotificationOverlay.hpp"
|
#include "../debug/HyprNotificationOverlay.hpp"
|
||||||
#include "../render/Renderer.hpp"
|
#include "../render/Renderer.hpp"
|
||||||
#include "../render/OpenGL.hpp"
|
#include "../render/OpenGL.hpp"
|
||||||
|
#include "../layout/space/Space.hpp"
|
||||||
|
#include "../layout/algorithm/Algorithm.hpp"
|
||||||
|
#include "../layout/algorithm/TiledAlgorithm.hpp"
|
||||||
|
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
|
||||||
|
|
||||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
#include <sys/ucred.h>
|
#include <sys/ucred.h>
|
||||||
|
|
@ -142,12 +149,12 @@ std::string CHyprCtl::getSolitaryBlockedReason(Hyprutils::Memory::CSharedPointer
|
||||||
|
|
||||||
const std::array<const char*, CMonitor::DS_CHECKS_COUNT> DS_REASONS_JSON = {
|
const std::array<const char*, CMonitor::DS_CHECKS_COUNT> DS_REASONS_JSON = {
|
||||||
"\"UNKNOWN\"", "\"USER\"", "\"WINDOWED\"", "\"CONTENT\"", "\"MIRROR\"", "\"RECORD\"", "\"SW\"",
|
"\"UNKNOWN\"", "\"USER\"", "\"WINDOWED\"", "\"CONTENT\"", "\"MIRROR\"", "\"RECORD\"", "\"SW\"",
|
||||||
"\"CANDIDATE\"", "\"SURFACE\"", "\"TRANSFORM\"", "\"DMA\"", "\"TEARING\"", "\"FAILED\"", "\"CM\"",
|
"\"CANDIDATE\"", "\"SURFACE\"", "\"TRANSFORM\"", "\"DMA\"", "\"FAILED\"", "\"CM\"",
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::array<const char*, CMonitor::DS_CHECKS_COUNT> DS_REASONS_TEXT = {
|
const std::array<const char*, CMonitor::DS_CHECKS_COUNT> DS_REASONS_TEXT = {
|
||||||
"unknown reason", "user settings", "windowed mode", "content type", "monitor mirrors", "screen record/screenshot", "software renders/cursors",
|
"unknown reason", "user settings", "windowed mode", "content type", "monitor mirrors", "screen record/screenshot", "software renders/cursors",
|
||||||
"missing candidate", "invalid surface", "surface transformations", "invalid buffer", "tearing", "activation failed", "color management",
|
"missing candidate", "invalid surface", "surface transformations", "invalid buffer", "activation failed", "color management",
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string CHyprCtl::getDSBlockedReason(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) {
|
std::string CHyprCtl::getDSBlockedReason(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) {
|
||||||
|
|
@ -170,12 +177,11 @@ std::string CHyprCtl::getDSBlockedReason(Hyprutils::Memory::CSharedPointer<CMoni
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<const char*, CMonitor::TC_CHECKS_COUNT> TEARING_REASONS_JSON = {
|
const std::array<const char*, CMonitor::TC_CHECKS_COUNT> TEARING_REASONS_JSON = {
|
||||||
"\"UNKNOWN\"", "\"NOT_TORN\"", "\"USER\"", "\"ZOOM\"", "\"SUPPORT\"", "\"CANDIDATE\"", "\"WINDOW\"",
|
"\"UNKNOWN\"", "\"NOT_TORN\"", "\"USER\"", "\"ZOOM\"", "\"SUPPORT\"", "\"CANDIDATE\"", "\"WINDOW\"", "\"HW_CURSOR\"",
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::array<const char*, CMonitor::TC_CHECKS_COUNT> TEARING_REASONS_TEXT = {
|
const std::array<const char*, CMonitor::TC_CHECKS_COUNT> TEARING_REASONS_TEXT = {"unknown reason", "next frame is not torn", "user settings", "zoom",
|
||||||
"unknown reason", "next frame is not torn", "user settings", "zoom", "not supported by monitor", "missing candidate", "window settings",
|
"not supported by monitor", "missing candidate", "window settings", "hw cursor"};
|
||||||
};
|
|
||||||
|
|
||||||
std::string CHyprCtl::getTearingBlockedReason(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) {
|
std::string CHyprCtl::getTearingBlockedReason(Hyprutils::Memory::CSharedPointer<CMonitor> m, eHyprCtlOutputFormat format) {
|
||||||
const auto reasons = m->isTearingBlocked(true);
|
const auto reasons = m->isTearingBlocked(true);
|
||||||
|
|
@ -253,13 +259,13 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
|
||||||
escapeJSONStrings(m->m_output->serial), sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), sc<int>(m->m_output->physicalSize.x),
|
escapeJSONStrings(m->m_output->serial), sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), sc<int>(m->m_output->physicalSize.x),
|
||||||
sc<int>(m->m_output->physicalSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->activeWorkspaceID(),
|
sc<int>(m->m_output->physicalSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->activeWorkspaceID(),
|
||||||
(!m->m_activeWorkspace ? "" : escapeJSONStrings(m->m_activeWorkspace->m_name)), m->activeSpecialWorkspaceID(),
|
(!m->m_activeWorkspace ? "" : escapeJSONStrings(m->m_activeWorkspace->m_name)), m->activeSpecialWorkspaceID(),
|
||||||
escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc<int>(m->m_reservedTopLeft.x), sc<int>(m->m_reservedTopLeft.y),
|
escapeJSONStrings(m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""), sc<int>(m->m_reservedArea.left()), sc<int>(m->m_reservedArea.top()),
|
||||||
sc<int>(m->m_reservedBottomRight.x), sc<int>(m->m_reservedBottomRight.y), m->m_scale, sc<int>(m->m_transform), (m == g_pCompositor->m_lastMonitor ? "true" : "false"),
|
sc<int>(m->m_reservedArea.right()), sc<int>(m->m_reservedArea.bottom()), m->m_scale, sc<int>(m->m_transform),
|
||||||
(m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"), rc<uint64_t>(m->m_solitaryClient.get()),
|
(m == Desktop::focusState()->monitor() ? "true" : "false"), (m->m_dpmsStatus ? "true" : "false"), (m->m_output->state->state().adaptiveSync ? "true" : "false"),
|
||||||
getSolitaryBlockedReason(m, format), (m->m_tearingState.activelyTearing ? "true" : "false"), getTearingBlockedReason(m, format), rc<uint64_t>(m->m_lastScanout.get()),
|
rc<uint64_t>(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), (m->m_tearingState.activelyTearing ? "true" : "false"),
|
||||||
getDSBlockedReason(m, format), (m->m_enabled ? "false" : "true"), formatToString(m->m_output->state->state().drmFormat),
|
getTearingBlockedReason(m, format), rc<uint64_t>(m->m_lastScanout.get()), getDSBlockedReason(m, format), (m->m_enabled ? "false" : "true"),
|
||||||
m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format), (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness),
|
formatToString(m->m_output->state->state().drmFormat), m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format),
|
||||||
(m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance));
|
(NCMType::toString(m->m_cmType)), (m->m_sdrBrightness), (m->m_sdrSaturation), (m->m_sdrMinLuminance), (m->m_sdrMaxLuminance));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
result += std::format(
|
result += std::format(
|
||||||
|
|
@ -272,8 +278,8 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
|
||||||
m->m_name, m->m_id, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->m_shortDescription,
|
m->m_name, m->m_id, sc<int>(m->m_pixelSize.x), sc<int>(m->m_pixelSize.y), m->m_refreshRate, sc<int>(m->m_position.x), sc<int>(m->m_position.y), m->m_shortDescription,
|
||||||
m->m_output->make, m->m_output->model, sc<int>(m->m_output->physicalSize.x), sc<int>(m->m_output->physicalSize.y), m->m_output->serial, m->activeWorkspaceID(),
|
m->m_output->make, m->m_output->model, sc<int>(m->m_output->physicalSize.x), sc<int>(m->m_output->physicalSize.y), m->m_output->serial, m->activeWorkspaceID(),
|
||||||
(!m->m_activeWorkspace ? "" : m->m_activeWorkspace->m_name), m->activeSpecialWorkspaceID(), (m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""),
|
(!m->m_activeWorkspace ? "" : m->m_activeWorkspace->m_name), m->activeSpecialWorkspaceID(), (m->m_activeSpecialWorkspace ? m->m_activeSpecialWorkspace->m_name : ""),
|
||||||
sc<int>(m->m_reservedTopLeft.x), sc<int>(m->m_reservedTopLeft.y), sc<int>(m->m_reservedBottomRight.x), sc<int>(m->m_reservedBottomRight.y), m->m_scale,
|
sc<int>(m->m_reservedArea.left()), sc<int>(m->m_reservedArea.top()), sc<int>(m->m_reservedArea.right()), sc<int>(m->m_reservedArea.bottom()), m->m_scale,
|
||||||
sc<int>(m->m_transform), (m == g_pCompositor->m_lastMonitor ? "yes" : "no"), sc<int>(m->m_dpmsStatus), m->m_output->state->state().adaptiveSync,
|
sc<int>(m->m_transform), (m == Desktop::focusState()->monitor() ? "yes" : "no"), sc<int>(m->m_dpmsStatus), m->m_output->state->state().adaptiveSync,
|
||||||
rc<uint64_t>(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), m->m_tearingState.activelyTearing, getTearingBlockedReason(m, format),
|
rc<uint64_t>(m->m_solitaryClient.get()), getSolitaryBlockedReason(m, format), m->m_tearingState.activelyTearing, getTearingBlockedReason(m, format),
|
||||||
rc<uint64_t>(m->m_lastScanout.get()), getDSBlockedReason(m, format), !m->m_enabled, formatToString(m->m_output->state->state().drmFormat),
|
rc<uint64_t>(m->m_lastScanout.get()), getDSBlockedReason(m, format), !m->m_enabled, formatToString(m->m_output->state->state().drmFormat),
|
||||||
m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format), (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness),
|
m->m_mirrorOf ? std::format("{}", m->m_mirrorOf->m_id) : "none", availableModesForOutput(m, format), (NCMType::toString(m->m_cmType)), (m->m_sdrBrightness),
|
||||||
|
|
@ -317,7 +323,7 @@ static std::string monitorsRequest(eHyprCtlOutputFormat format, std::string requ
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||||
const auto tags = w->m_tags.getTags();
|
const auto tags = w->m_ruleApplicator->m_tagKeeper.getTags();
|
||||||
|
|
||||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
|
if (format == eHyprCtlOutputFormat::FORMAT_JSON)
|
||||||
return std::ranges::fold_left(tags, std::string(),
|
return std::ranges::fold_left(tags, std::string(),
|
||||||
|
|
@ -328,22 +334,18 @@ static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||||
|
|
||||||
static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||||
const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
|
const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
|
||||||
if (w->m_groupData.pNextWindow.expired())
|
if (!w->m_group)
|
||||||
return isJson ? "" : "0";
|
return isJson ? "" : "0";
|
||||||
|
|
||||||
std::ostringstream result;
|
std::ostringstream result;
|
||||||
|
|
||||||
PHLWINDOW head = w->getGroupHead();
|
for (const auto& curr : w->m_group->windows()) {
|
||||||
PHLWINDOW curr = head;
|
|
||||||
while (true) {
|
|
||||||
if (isJson)
|
if (isJson)
|
||||||
result << std::format("\"0x{:x}\"", rc<uintptr_t>(curr.get()));
|
result << std::format("\"0x{:x}\"", rc<uintptr_t>(curr.get()));
|
||||||
else
|
else
|
||||||
result << std::format("{:x}", rc<uintptr_t>(curr.get()));
|
result << std::format("{:x}", rc<uintptr_t>(curr.get()));
|
||||||
curr = curr->m_groupData.pNextWindow.lock();
|
|
||||||
// We've wrapped around to the start, break out without trailing comma
|
if (curr != w->m_group->windows().back())
|
||||||
if (curr == head)
|
|
||||||
break;
|
|
||||||
result << (isJson ? ", " : ",");
|
result << (isJson ? ", " : ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,9 +354,10 @@ static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||||
|
|
||||||
std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||||
auto getFocusHistoryID = [](PHLWINDOW wnd) -> int {
|
auto getFocusHistoryID = [](PHLWINDOW wnd) -> int {
|
||||||
for (size_t i = 0; i < g_pCompositor->m_windowFocusHistory.size(); ++i) {
|
const auto& HISTORY = Desktop::History::windowTracker()->fullHistory();
|
||||||
if (g_pCompositor->m_windowFocusHistory[i].lock() == wnd)
|
for (size_t i = 0; i < HISTORY.size(); ++i) {
|
||||||
return i;
|
if (HISTORY[i].lock() == wnd)
|
||||||
|
return HISTORY.size() - i - 1; // reverse order for backwards compat
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
@ -372,7 +375,6 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||||
"name": "{}"
|
"name": "{}"
|
||||||
}},
|
}},
|
||||||
"floating": {},
|
"floating": {},
|
||||||
"pseudo": {},
|
|
||||||
"monitor": {},
|
"monitor": {},
|
||||||
"class": "{}",
|
"class": "{}",
|
||||||
"title": "{}",
|
"title": "{}",
|
||||||
|
|
@ -383,34 +385,39 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||||
"pinned": {},
|
"pinned": {},
|
||||||
"fullscreen": {},
|
"fullscreen": {},
|
||||||
"fullscreenClient": {},
|
"fullscreenClient": {},
|
||||||
|
"overFullscreen": {},
|
||||||
"grouped": [{}],
|
"grouped": [{}],
|
||||||
"tags": [{}],
|
"tags": [{}],
|
||||||
"swallowing": "0x{:x}",
|
"swallowing": "0x{:x}",
|
||||||
"focusHistoryID": {},
|
"focusHistoryID": {},
|
||||||
"inhibitingIdle": {},
|
"inhibitingIdle": {},
|
||||||
"xdgTag": "{}",
|
"xdgTag": "{}",
|
||||||
"xdgDescription": "{}"
|
"xdgDescription": "{}",
|
||||||
|
"contentType": "{}",
|
||||||
|
"stableId": "{:x}"
|
||||||
}},)#",
|
}},)#",
|
||||||
rc<uintptr_t>(w.get()), (w->m_isMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), sc<int>(w->m_realPosition->goal().x),
|
rc<uintptr_t>(w.get()), (w->m_isMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), sc<int>(w->m_realPosition->goal().x),
|
||||||
sc<int>(w->m_realPosition->goal().y), sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
sc<int>(w->m_realPosition->goal().y), sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
||||||
escapeJSONStrings(!w->m_workspace ? "" : w->m_workspace->m_name), (sc<int>(w->m_isFloating) == 1 ? "true" : "false"), (w->m_isPseudotiled ? "true" : "false"),
|
escapeJSONStrings(!w->m_workspace ? "" : w->m_workspace->m_name), (sc<int>(w->m_isFloating) == 1 ? "true" : "false"), w->monitorID(), escapeJSONStrings(w->m_class),
|
||||||
w->monitorID(), escapeJSONStrings(w->m_class), escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle), w->getPID(),
|
escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle), w->getPID(), (sc<int>(w->m_isX11) == 1 ? "true" : "false"),
|
||||||
(sc<int>(w->m_isX11) == 1 ? "true" : "false"), (w->m_pinned ? "true" : "false"), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client),
|
(w->m_pinned ? "true" : "false"), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client), (w->m_createdOverFullscreen ? "true" : "false"),
|
||||||
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w),
|
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w),
|
||||||
(g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")));
|
(g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")),
|
||||||
|
escapeJSONStrings(NContentType::toString(w->getContentType())), w->m_stableID);
|
||||||
} else {
|
} else {
|
||||||
return std::format(
|
return std::format(
|
||||||
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
|
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
|
||||||
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
|
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
|
||||||
"{}\n\txwayland: {}\n\tpinned: "
|
"{}\n\txwayland: {}\n\tpinned: "
|
||||||
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: {}\n\txdgTag: "
|
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\toverFullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: "
|
||||||
"{}\n\txdgDescription: {}\n\n",
|
"{}\n\txdgTag: "
|
||||||
|
"{}\n\txdgDescription: {}\n\tcontentType: {}\n\tstableID: {:x}\n\n",
|
||||||
rc<uintptr_t>(w.get()), w->m_title, sc<int>(w->m_isMapped), sc<int>(w->isHidden()), sc<int>(w->m_realPosition->goal().x), sc<int>(w->m_realPosition->goal().y),
|
rc<uintptr_t>(w.get()), w->m_title, sc<int>(w->m_isMapped), sc<int>(w->isHidden()), sc<int>(w->m_realPosition->goal().x), sc<int>(w->m_realPosition->goal().y),
|
||||||
sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
||||||
(!w->m_workspace ? "" : w->m_workspace->m_name), sc<int>(w->m_isFloating), sc<int>(w->m_isPseudotiled), w->monitorID(), w->m_class, w->m_title, w->m_initialClass,
|
(!w->m_workspace ? "" : w->m_workspace->m_name), sc<int>(w->m_isFloating), w->monitorID(), w->m_class, w->m_title, w->m_initialClass, w->m_initialTitle, w->getPID(),
|
||||||
w->m_initialTitle, w->getPID(), sc<int>(w->m_isX11), sc<int>(w->m_pinned), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client),
|
sc<int>(w->m_isX11), sc<int>(w->m_pinned), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client), sc<int>(w->m_createdOverFullscreen),
|
||||||
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w), sc<int>(g_pInputManager->isWindowInhibiting(w, false)),
|
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w), sc<int>(g_pInputManager->isWindowInhibiting(w, false)),
|
||||||
w->xdgTag().value_or(""), w->xdgDescription().value_or(""));
|
w->xdgTag().value_or(""), w->xdgDescription().value_or(""), NContentType::toString(w->getContentType()), w->m_stableID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -446,6 +453,13 @@ static std::string clientsRequest(eHyprCtlOutputFormat format, std::string reque
|
||||||
std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) {
|
std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) {
|
||||||
const auto PLASTW = w->getLastFocusedWindow();
|
const auto PLASTW = w->getLastFocusedWindow();
|
||||||
const auto PMONITOR = w->m_monitor.lock();
|
const auto PMONITOR = w->m_monitor.lock();
|
||||||
|
|
||||||
|
std::string layoutName = "unknown";
|
||||||
|
if (w->m_space && w->m_space->algorithm() && w->m_space->algorithm()->tiledAlgo()) {
|
||||||
|
const auto& TILED_ALGO = w->m_space->algorithm()->tiledAlgo();
|
||||||
|
layoutName = Layout::Supplementary::algoMatcher()->getNameForTiledAlgo(&typeid(*TILED_ALGO.get()));
|
||||||
|
}
|
||||||
|
|
||||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
||||||
return std::format(R"#({{
|
return std::format(R"#({{
|
||||||
"id": {},
|
"id": {},
|
||||||
|
|
@ -456,16 +470,17 @@ std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat form
|
||||||
"hasfullscreen": {},
|
"hasfullscreen": {},
|
||||||
"lastwindow": "0x{:x}",
|
"lastwindow": "0x{:x}",
|
||||||
"lastwindowtitle": "{}",
|
"lastwindowtitle": "{}",
|
||||||
"ispersistent": {}
|
"ispersistent": {},
|
||||||
|
"tiledLayout": "{}"
|
||||||
}})#",
|
}})#",
|
||||||
w->m_id, escapeJSONStrings(w->m_name), escapeJSONStrings(PMONITOR ? PMONITOR->m_name : "?"),
|
w->m_id, escapeJSONStrings(w->m_name), escapeJSONStrings(PMONITOR ? PMONITOR->m_name : "?"),
|
||||||
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->m_id) : "null"), w->getWindows(), w->m_hasFullscreenWindow ? "true" : "false",
|
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->m_id) : "null"), w->getWindows(), w->m_hasFullscreenWindow ? "true" : "false",
|
||||||
rc<uintptr_t>(PLASTW.get()), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->isPersistent() ? "true" : "false");
|
rc<uintptr_t>(PLASTW.get()), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->isPersistent() ? "true" : "false", escapeJSONStrings(layoutName));
|
||||||
} else {
|
} else {
|
||||||
return std::format(
|
return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: "
|
||||||
"workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: {}\n\n",
|
"{}\n\ttiledLayout: {}\n\n",
|
||||||
w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindows(), sc<int>(w->m_hasFullscreenWindow),
|
w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindows(),
|
||||||
rc<uintptr_t>(PLASTW.get()), PLASTW ? PLASTW->m_title : "", sc<int>(w->isPersistent()));
|
sc<int>(w->m_hasFullscreenWindow), rc<uintptr_t>(PLASTW.get()), PLASTW ? PLASTW->m_title : "", sc<int>(w->isPersistent()), layoutName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,11 +536,11 @@ static std::string getWorkspaceRuleData(const SWorkspaceRule& r, eHyprCtlOutputF
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
|
static std::string activeWorkspaceRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||||
if (!g_pCompositor->m_lastMonitor)
|
if (!Desktop::focusState()->monitor())
|
||||||
return "unsafe state";
|
return "unsafe state";
|
||||||
|
|
||||||
std::string result = "";
|
std::string result = "";
|
||||||
auto w = g_pCompositor->m_lastMonitor->m_activeWorkspace;
|
auto w = Desktop::focusState()->monitor()->m_activeWorkspace;
|
||||||
|
|
||||||
if (!valid(w))
|
if (!valid(w))
|
||||||
return "internal error";
|
return "internal error";
|
||||||
|
|
@ -575,7 +590,7 @@ static std::string workspaceRulesRequest(eHyprCtlOutputFormat format, std::strin
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
|
static std::string activeWindowRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||||
const auto PWINDOW = g_pCompositor->m_lastWindow.lock();
|
const auto PWINDOW = Desktop::focusState()->window();
|
||||||
|
|
||||||
if (!validMapped(PWINDOW))
|
if (!validMapped(PWINDOW))
|
||||||
return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
|
return format == eHyprCtlOutputFormat::FORMAT_JSON ? "{}" : "Invalid";
|
||||||
|
|
@ -664,28 +679,6 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
|
|
||||||
std::string result = "";
|
|
||||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
|
||||||
result += "[";
|
|
||||||
|
|
||||||
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
|
|
||||||
result += std::format(
|
|
||||||
R"#(
|
|
||||||
"{}",)#",
|
|
||||||
m);
|
|
||||||
}
|
|
||||||
trimTrailingComma(result);
|
|
||||||
|
|
||||||
result += "\n]\n";
|
|
||||||
} else {
|
|
||||||
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
|
|
||||||
result += std::format("{}\n", m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) {
|
static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||||
std::string result = "";
|
std::string result = "";
|
||||||
std::string currErrors = g_pConfigManager->getErrors();
|
std::string currErrors = g_pConfigManager->getErrors();
|
||||||
|
|
@ -796,7 +789,7 @@ static std::string devicesRequest(eHyprCtlOutputFormat format, std::string reque
|
||||||
result += std::format(
|
result += std::format(
|
||||||
R"#( {{
|
R"#( {{
|
||||||
"address": "0x{:x}",
|
"address": "0x{:x}",
|
||||||
"type": "tabletTool",
|
"type": "tabletTool"
|
||||||
}},)#",
|
}},)#",
|
||||||
rc<uintptr_t>(d.get()));
|
rc<uintptr_t>(d.get()));
|
||||||
}
|
}
|
||||||
|
|
@ -953,11 +946,10 @@ static std::string rollinglogRequest(eHyprCtlOutputFormat format, std::string re
|
||||||
|
|
||||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
||||||
result += "[\n\"log\":\"";
|
result += "[\n\"log\":\"";
|
||||||
result += escapeJSONStrings(Debug::m_rollingLog);
|
result += escapeJSONStrings(Log::logger->rolling());
|
||||||
result += "\"]";
|
result += "\"]";
|
||||||
} else {
|
} else
|
||||||
result = Debug::m_rollingLog;
|
result = Log::logger->rolling();
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -1025,6 +1017,7 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request
|
||||||
"has_description": {},
|
"has_description": {},
|
||||||
"modmask": {},
|
"modmask": {},
|
||||||
"submap": "{}",
|
"submap": "{}",
|
||||||
|
"submap_universal": "{}",
|
||||||
"key": "{}",
|
"key": "{}",
|
||||||
"keycode": {},
|
"keycode": {},
|
||||||
"catch_all": {},
|
"catch_all": {},
|
||||||
|
|
@ -1033,8 +1026,9 @@ static std::string bindsRequest(eHyprCtlOutputFormat format, std::string request
|
||||||
"arg": "{}"
|
"arg": "{}"
|
||||||
}},)#",
|
}},)#",
|
||||||
kb->locked ? "true" : "false", kb->mouse ? "true" : "false", kb->release ? "true" : "false", kb->repeat ? "true" : "false", kb->longPress ? "true" : "false",
|
kb->locked ? "true" : "false", kb->mouse ? "true" : "false", kb->release ? "true" : "false", kb->repeat ? "true" : "false", kb->longPress ? "true" : "false",
|
||||||
kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap.name), escapeJSONStrings(kb->key),
|
kb->nonConsuming ? "true" : "false", kb->hasDescription ? "true" : "false", kb->modmask, escapeJSONStrings(kb->submap.name), kb->submapUniversal,
|
||||||
kb->keycode, kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler), escapeJSONStrings(kb->arg));
|
escapeJSONStrings(kb->key), kb->keycode, kb->catchAll ? "true" : "false", escapeJSONStrings(kb->description), escapeJSONStrings(kb->handler),
|
||||||
|
escapeJSONStrings(kb->arg));
|
||||||
}
|
}
|
||||||
trimTrailingComma(ret);
|
trimTrailingComma(ret);
|
||||||
ret += "]";
|
ret += "]";
|
||||||
|
|
@ -1057,8 +1051,11 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||||
result += "\n";
|
result += "\n";
|
||||||
result += getBuiltSystemLibraryNames();
|
result += getBuiltSystemLibraryNames();
|
||||||
result += "\n";
|
result += "\n";
|
||||||
|
result += "Version ABI string: ";
|
||||||
|
result += __hyprland_api_get_hash();
|
||||||
|
result += "\n";
|
||||||
|
|
||||||
#if (!ISDEBUG && !defined(NO_XWAYLAND))
|
#if (!ISDEBUG && !defined(NO_XWAYLAND) && !defined(BUILT_WITH_NIX))
|
||||||
result += "no flags were set\n";
|
result += "no flags were set\n";
|
||||||
#else
|
#else
|
||||||
result += "flags set:\n";
|
result += "flags set:\n";
|
||||||
|
|
@ -1068,6 +1065,9 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||||
#ifdef NO_XWAYLAND
|
#ifdef NO_XWAYLAND
|
||||||
result += "no xwayland\n";
|
result += "no xwayland\n";
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef BUILT_WITH_NIX
|
||||||
|
result += "nix\n";
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1091,10 +1091,12 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||||
"systemHyprutils": "{}",
|
"systemHyprutils": "{}",
|
||||||
"systemHyprcursor": "{}",
|
"systemHyprcursor": "{}",
|
||||||
"systemHyprgraphics": "{}",
|
"systemHyprgraphics": "{}",
|
||||||
|
"abiHash": "{}",
|
||||||
"flags": [)#",
|
"flags": [)#",
|
||||||
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
|
GIT_BRANCH, GIT_COMMIT_HASH, HYPRLAND_VERSION, (strcmp(GIT_DIRTY, "dirty") == 0 ? "true" : "false"), escapeJSONStrings(commitMsg), GIT_COMMIT_DATE, GIT_TAG,
|
||||||
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION, getSystemLibraryVersion("aquamarine"),
|
GIT_COMMITS, AQUAMARINE_VERSION, HYPRLANG_VERSION, HYPRUTILS_VERSION, HYPRCURSOR_VERSION, HYPRGRAPHICS_VERSION, getSystemLibraryVersion("aquamarine"),
|
||||||
getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics"));
|
getSystemLibraryVersion("hyprlang"), getSystemLibraryVersion("hyprutils"), getSystemLibraryVersion("hyprcursor"), getSystemLibraryVersion("hyprgraphics"),
|
||||||
|
__hyprland_api_get_hash());
|
||||||
|
|
||||||
#if ISDEBUG
|
#if ISDEBUG
|
||||||
result += "\"debug\",";
|
result += "\"debug\",";
|
||||||
|
|
@ -1102,6 +1104,9 @@ std::string versionRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||||
#ifdef NO_XWAYLAND
|
#ifdef NO_XWAYLAND
|
||||||
result += "\"no xwayland\",";
|
result += "\"no xwayland\",";
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef BUILT_WITH_NIX
|
||||||
|
result += "\"nix\",";
|
||||||
|
#endif
|
||||||
|
|
||||||
trimTrailingComma(result);
|
trimTrailingComma(result);
|
||||||
|
|
||||||
|
|
@ -1243,7 +1248,7 @@ static std::string dispatchRequest(eHyprCtlOutputFormat format, std::string in)
|
||||||
|
|
||||||
SDispatchResult res = DISPATCHER->second(DISPATCHARG);
|
SDispatchResult res = DISPATCHER->second(DISPATCHARG);
|
||||||
|
|
||||||
Debug::log(LOG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error);
|
Log::logger->log(Log::DEBUG, "Hyprctl: dispatcher {} : {}{}", DISPATCHSTR, DISPATCHARG, res.success ? "" : " -> " + res.error);
|
||||||
|
|
||||||
return res.success ? "ok" : res.error;
|
return res.success ? "ok" : res.error;
|
||||||
}
|
}
|
||||||
|
|
@ -1270,10 +1275,19 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
|
||||||
if (COMMAND.empty())
|
if (COMMAND.empty())
|
||||||
return "Invalid input: command is empty";
|
return "Invalid input: command is empty";
|
||||||
|
|
||||||
|
g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = true;
|
||||||
|
|
||||||
std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE);
|
std::string retval = g_pConfigManager->parseKeyword(COMMAND, VALUE);
|
||||||
|
|
||||||
|
g_pHyprCtl->m_currentRequestParams.isDynamicKeyword = false;
|
||||||
|
|
||||||
|
if (COMMAND == "source") {
|
||||||
|
g_pConfigManager->m_wantsMonitorReload = true;
|
||||||
|
g_pEventLoopManager->doLater([] { g_pConfigManager->reloadRules(); });
|
||||||
|
}
|
||||||
|
|
||||||
// if we are executing a dynamic source we have to reload everything, so every if will have a check for source.
|
// if we are executing a dynamic source we have to reload everything, so every if will have a check for source.
|
||||||
if (COMMAND == "monitor" || COMMAND == "source")
|
if (COMMAND == "monitor")
|
||||||
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
|
g_pConfigManager->m_wantsMonitorReload = true; // for monitor keywords
|
||||||
|
|
||||||
if (COMMAND.contains("monitorv2"))
|
if (COMMAND.contains("monitorv2"))
|
||||||
|
|
@ -1286,10 +1300,8 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
|
||||||
g_pInputManager->setTabletConfigs(); // update tablets
|
g_pInputManager->setTabletConfigs(); // update tablets
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto PLAYOUT = CConfigValue<std::string>("general:layout");
|
if (COMMAND.contains("general:layout") || (COMMAND.contains("workspace") && VALUE.contains("layout:")))
|
||||||
|
Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts();
|
||||||
if (COMMAND.contains("general:layout"))
|
|
||||||
g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout
|
|
||||||
|
|
||||||
if (COMMAND.contains("decoration:screen_shader") || COMMAND == "source")
|
if (COMMAND.contains("decoration:screen_shader") || COMMAND == "source")
|
||||||
g_pHyprOpenGL->m_reloadScreenShader = true;
|
g_pHyprOpenGL->m_reloadScreenShader = true;
|
||||||
|
|
@ -1304,20 +1316,31 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
|
||||||
g_pConfigManager->updateWatcher();
|
g_pConfigManager->updateWatcher();
|
||||||
|
|
||||||
// decorations will probably need a repaint
|
// decorations will probably need a repaint
|
||||||
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source" ||
|
if (COMMAND.contains("decoration:") || COMMAND.contains("border") || COMMAND == "workspace" || COMMAND.contains("zoom_factor") || COMMAND == "source") {
|
||||||
COMMAND.starts_with("windowrule")) {
|
|
||||||
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
||||||
for (auto const& m : g_pCompositor->m_monitors) {
|
for (auto const& m : g_pCompositor->m_monitors) {
|
||||||
*(m->m_cursorZoom) = *PZOOMFACTOR;
|
*(m->m_cursorZoom) = *PZOOMFACTOR;
|
||||||
g_pHyprRenderer->damageMonitor(m);
|
g_pHyprRenderer->damageMonitor(m);
|
||||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
|
if (m->m_activeWorkspace)
|
||||||
|
m->m_activeWorkspace->m_space->recalculate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COMMAND.contains("windowrule ") || COMMAND.contains("windowrule["))
|
||||||
|
g_pConfigManager->reloadRules();
|
||||||
|
|
||||||
|
if (COMMAND.contains("layerrule") || COMMAND.contains("layerrule[")) {
|
||||||
|
g_pConfigManager->reloadRules();
|
||||||
|
// Damage all monitors to redraw static layers.
|
||||||
|
for (auto const& m : g_pCompositor->m_monitors) {
|
||||||
|
g_pHyprRenderer->damageMonitor(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (COMMAND.contains("workspace"))
|
if (COMMAND.contains("workspace"))
|
||||||
g_pConfigManager->ensurePersistentWorkspacesPresent();
|
g_pConfigManager->ensurePersistentWorkspacesPresent();
|
||||||
|
|
||||||
Debug::log(LOG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);
|
Log::logger->log(Log::DEBUG, "Hyprctl: keyword {} : {}", COMMAND, VALUE);
|
||||||
|
|
||||||
if (retval.empty())
|
if (retval.empty())
|
||||||
return "ok";
|
return "ok";
|
||||||
|
|
@ -1519,11 +1542,6 @@ static std::string dispatchSeterror(eHyprCtlOutputFormat format, std::string req
|
||||||
return "ok";
|
return "ok";
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string dispatchSetProp(eHyprCtlOutputFormat format, std::string request) {
|
|
||||||
auto result = g_pKeybindManager->m_dispatchers["setprop"](request.substr(request.find_first_of(' ') + 1));
|
|
||||||
return "DEPRECATED: use hyprctl dispatch setprop instead" + (result.success ? "" : "\n" + result.error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) {
|
static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string request) {
|
||||||
CVarList vars(request, 0, ' ');
|
CVarList vars(request, 0, ' ');
|
||||||
|
|
||||||
|
|
@ -1541,9 +1559,9 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
||||||
const bool FORMNORM = format == FORMAT_NORMAL;
|
const bool FORMNORM = format == FORMAT_NORMAL;
|
||||||
|
|
||||||
auto sizeToString = [&](bool max) -> std::string {
|
auto sizeToString = [&](bool max) -> std::string {
|
||||||
auto sizeValue = PWINDOW->m_windowData.minSize.valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
|
auto sizeValue = PWINDOW->m_ruleApplicator->minSize().valueOr(Vector2D(MIN_WINDOW_SIZE, MIN_WINDOW_SIZE));
|
||||||
if (max)
|
if (max)
|
||||||
sizeValue = PWINDOW->m_windowData.maxSize.valueOr(Vector2D(INFINITY, INFINITY));
|
sizeValue = PWINDOW->m_ruleApplicator->maxSize().valueOr(Vector2D(INFINITY, INFINITY));
|
||||||
|
|
||||||
if (FORMNORM)
|
if (FORMNORM)
|
||||||
return std::format("{} {}", sizeValue.x, sizeValue.y);
|
return std::format("{} {}", sizeValue.x, sizeValue.y);
|
||||||
|
|
@ -1554,7 +1572,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto alphaToString = [&](CWindowOverridableVar<SAlphaValue>& alpha, bool getAlpha) -> std::string {
|
auto alphaToString = [&](Desktop::Types::COverridableVar<Desktop::Types::SAlphaValue>& alpha, bool getAlpha) -> std::string {
|
||||||
if (FORMNORM) {
|
if (FORMNORM) {
|
||||||
if (getAlpha)
|
if (getAlpha)
|
||||||
return std::format("{}", alpha.valueOrDefault().alpha);
|
return std::format("{}", alpha.valueOrDefault().alpha);
|
||||||
|
|
@ -1578,7 +1596,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
||||||
static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
|
static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
|
||||||
static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
|
static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
|
||||||
|
|
||||||
const bool GROUPLOCKED = PWINDOW->m_groupData.pNextWindow.lock() ? PWINDOW->getGroupHead()->m_groupData.locked : false;
|
const bool GROUPLOCKED = PWINDOW->m_group ? PWINDOW->m_group->locked() : false;
|
||||||
|
|
||||||
if (active) {
|
if (active) {
|
||||||
auto* const ACTIVECOL = (CGradientValueData*)(PACTIVECOL.ptr())->getData();
|
auto* const ACTIVECOL = (CGradientValueData*)(PACTIVECOL.ptr())->getData();
|
||||||
|
|
@ -1586,9 +1604,9 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
||||||
auto* const GROUPACTIVECOL = (CGradientValueData*)(PGROUPACTIVECOL.ptr())->getData();
|
auto* const GROUPACTIVECOL = (CGradientValueData*)(PGROUPACTIVECOL.ptr())->getData();
|
||||||
auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPACTIVELOCKEDCOL.ptr())->getData();
|
auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPACTIVELOCKEDCOL.ptr())->getData();
|
||||||
const auto* const ACTIVECOLOR =
|
const auto* const ACTIVECOLOR =
|
||||||
!PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
|
!PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
|
||||||
|
|
||||||
std::string borderColorString = PWINDOW->m_windowData.activeBorderColor.valueOr(*ACTIVECOLOR).toString();
|
std::string borderColorString = PWINDOW->m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR).toString();
|
||||||
if (FORMNORM)
|
if (FORMNORM)
|
||||||
return borderColorString;
|
return borderColorString;
|
||||||
else
|
else
|
||||||
|
|
@ -1598,10 +1616,10 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
||||||
auto* const NOGROUPINACTIVECOL = (CGradientValueData*)(PNOGROUPINACTIVECOL.ptr())->getData();
|
auto* const NOGROUPINACTIVECOL = (CGradientValueData*)(PNOGROUPINACTIVECOL.ptr())->getData();
|
||||||
auto* const GROUPINACTIVECOL = (CGradientValueData*)(PGROUPINACTIVECOL.ptr())->getData();
|
auto* const GROUPINACTIVECOL = (CGradientValueData*)(PGROUPINACTIVECOL.ptr())->getData();
|
||||||
auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPINACTIVELOCKEDCOL.ptr())->getData();
|
auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPINACTIVELOCKEDCOL.ptr())->getData();
|
||||||
const auto* const INACTIVECOLOR = !PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) :
|
const auto* const INACTIVECOLOR = !PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? INACTIVECOL : NOGROUPINACTIVECOL) :
|
||||||
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
|
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
|
||||||
|
|
||||||
std::string borderColorString = PWINDOW->m_windowData.inactiveBorderColor.valueOr(*INACTIVECOLOR).toString();
|
std::string borderColorString = PWINDOW->m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR).toString();
|
||||||
if (FORMNORM)
|
if (FORMNORM)
|
||||||
return borderColorString;
|
return borderColorString;
|
||||||
else
|
else
|
||||||
|
|
@ -1616,38 +1634,92 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
||||||
return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
|
return std::format(R"({{"{}": {}}})", PROP, prop.valueOrDefault());
|
||||||
};
|
};
|
||||||
|
|
||||||
if (PROP == "animationstyle") {
|
if (PROP == "animation") {
|
||||||
auto& animationStyle = PWINDOW->m_windowData.animationStyle;
|
auto& animationStyle = PWINDOW->m_ruleApplicator->animationStyle();
|
||||||
if (FORMNORM)
|
if (FORMNORM)
|
||||||
return animationStyle.valueOr("(unset)");
|
return animationStyle.valueOr("(unset)");
|
||||||
else
|
else
|
||||||
return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
|
return std::format(R"({{"{}": "{}"}})", PROP, animationStyle.valueOr(""));
|
||||||
} else if (PROP == "maxsize")
|
} else if (PROP == "max_size")
|
||||||
return sizeToString(true);
|
return sizeToString(true);
|
||||||
else if (PROP == "minsize")
|
else if (PROP == "min_size")
|
||||||
return sizeToString(false);
|
return sizeToString(false);
|
||||||
else if (PROP == "alpha")
|
else if (PROP == "opacity")
|
||||||
return alphaToString(PWINDOW->m_windowData.alpha, true);
|
return alphaToString(PWINDOW->m_ruleApplicator->alpha(), true);
|
||||||
else if (PROP == "alphainactive")
|
else if (PROP == "opacity_inactive")
|
||||||
return alphaToString(PWINDOW->m_windowData.alphaInactive, true);
|
return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), true);
|
||||||
else if (PROP == "alphafullscreen")
|
else if (PROP == "opacity_fullscreen")
|
||||||
return alphaToString(PWINDOW->m_windowData.alphaFullscreen, true);
|
return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), true);
|
||||||
else if (PROP == "alphaoverride")
|
else if (PROP == "opacity_override")
|
||||||
return alphaToString(PWINDOW->m_windowData.alpha, false);
|
return alphaToString(PWINDOW->m_ruleApplicator->alpha(), false);
|
||||||
else if (PROP == "alphainactiveoverride")
|
else if (PROP == "opacity_inactive_override")
|
||||||
return alphaToString(PWINDOW->m_windowData.alphaInactive, false);
|
return alphaToString(PWINDOW->m_ruleApplicator->alphaInactive(), false);
|
||||||
else if (PROP == "alphafullscreenoverride")
|
else if (PROP == "opacity_fullscreen_override")
|
||||||
return alphaToString(PWINDOW->m_windowData.alphaFullscreen, false);
|
return alphaToString(PWINDOW->m_ruleApplicator->alphaFullscreen(), false);
|
||||||
else if (PROP == "activebordercolor")
|
else if (PROP == "active_border_color")
|
||||||
return borderColorToString(true);
|
return borderColorToString(true);
|
||||||
else if (PROP == "inactivebordercolor")
|
else if (PROP == "inactive_border_color")
|
||||||
return borderColorToString(false);
|
return borderColorToString(false);
|
||||||
else if (auto search = NWindowProperties::boolWindowProperties.find(PROP); search != NWindowProperties::boolWindowProperties.end())
|
else if (PROP == "allows_input")
|
||||||
return windowPropToString(*search->second(PWINDOW));
|
return windowPropToString(PWINDOW->m_ruleApplicator->allowsInput());
|
||||||
else if (auto search = NWindowProperties::intWindowProperties.find(PROP); search != NWindowProperties::intWindowProperties.end())
|
else if (PROP == "decorate")
|
||||||
return windowPropToString(*search->second(PWINDOW));
|
return windowPropToString(PWINDOW->m_ruleApplicator->decorate());
|
||||||
else if (auto search = NWindowProperties::floatWindowProperties.find(PROP); search != NWindowProperties::floatWindowProperties.end())
|
else if (PROP == "focus_on_activate")
|
||||||
return windowPropToString(*search->second(PWINDOW));
|
return windowPropToString(PWINDOW->m_ruleApplicator->focusOnActivate());
|
||||||
|
else if (PROP == "keep_aspect_ratio")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->keepAspectRatio());
|
||||||
|
else if (PROP == "nearest_neighbor")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->nearestNeighbor());
|
||||||
|
else if (PROP == "no_anim")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noAnim());
|
||||||
|
else if (PROP == "no_blur")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noBlur());
|
||||||
|
else if (PROP == "no_dim")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noDim());
|
||||||
|
else if (PROP == "no_focus")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noFocus());
|
||||||
|
else if (PROP == "no_max_size")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noMaxSize());
|
||||||
|
else if (PROP == "no_shadow")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noShadow());
|
||||||
|
else if (PROP == "no_shortcuts_inhibit")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noShortcutsInhibit());
|
||||||
|
else if (PROP == "opaque")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->opaque());
|
||||||
|
else if (PROP == "dim_around")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->dimAround());
|
||||||
|
else if (PROP == "force_rgbx")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->RGBX());
|
||||||
|
else if (PROP == "sync_fullscreen")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->syncFullscreen());
|
||||||
|
else if (PROP == "immediate")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->tearing());
|
||||||
|
else if (PROP == "xray")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->xray());
|
||||||
|
else if (PROP == "render_unfocused")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->renderUnfocused());
|
||||||
|
else if (PROP == "no_follow_mouse")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noFollowMouse());
|
||||||
|
else if (PROP == "no_screen_share")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noScreenShare());
|
||||||
|
else if (PROP == "no_vrr")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->noVRR());
|
||||||
|
else if (PROP == "persistent_size")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->persistentSize());
|
||||||
|
else if (PROP == "stay_focused")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->stayFocused());
|
||||||
|
else if (PROP == "idle_inhibit")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->idleInhibitMode());
|
||||||
|
else if (PROP == "border_size")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->borderSize());
|
||||||
|
else if (PROP == "rounding")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->rounding());
|
||||||
|
else if (PROP == "rounding_power")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->roundingPower());
|
||||||
|
else if (PROP == "scroll_mouse")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->scrollMouse());
|
||||||
|
else if (PROP == "scroll_touchpad")
|
||||||
|
return windowPropToString(PWINDOW->m_ruleApplicator->scrollTouchpad());
|
||||||
|
|
||||||
return "prop not found";
|
return "prop not found";
|
||||||
}
|
}
|
||||||
|
|
@ -1977,7 +2049,12 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
|
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) {
|
||||||
if (g_pHyprOpenGL->initShaders())
|
CVarList vars(request, 0, ' ');
|
||||||
|
|
||||||
|
if (vars.size() > 2)
|
||||||
|
return "too many args";
|
||||||
|
|
||||||
|
if (g_pHyprOpenGL && g_pHyprRenderer->reloadShaders(vars.size() == 2 ? vars[1] : ""))
|
||||||
return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
|
return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
|
||||||
else
|
else
|
||||||
return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
|
return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
|
||||||
|
|
@ -2000,19 +2077,17 @@ CHyprCtl::CHyprCtl() {
|
||||||
registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest});
|
registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest});
|
||||||
registerCommand(SHyprCtlCommand{"animations", true, animationsRequest});
|
registerCommand(SHyprCtlCommand{"animations", true, animationsRequest});
|
||||||
registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
|
registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
|
||||||
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
|
|
||||||
registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
|
registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
|
||||||
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
|
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
|
||||||
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
|
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
|
||||||
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
|
registerCommand(SHyprCtlCommand{"submap", true, submapRequest});
|
||||||
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = true, .fn = reloadShaders});
|
|
||||||
|
|
||||||
|
registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = false, .fn = reloadShaders});
|
||||||
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
|
registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest});
|
||||||
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
|
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
|
||||||
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
|
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});
|
||||||
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
|
registerCommand(SHyprCtlCommand{"notify", false, dispatchNotify});
|
||||||
registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
|
registerCommand(SHyprCtlCommand{"dismissnotify", false, dispatchDismissNotify});
|
||||||
registerCommand(SHyprCtlCommand{"setprop", false, dispatchSetProp});
|
|
||||||
registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp});
|
registerCommand(SHyprCtlCommand{"getprop", false, dispatchGetProp});
|
||||||
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
|
registerCommand(SHyprCtlCommand{"seterror", false, dispatchSeterror});
|
||||||
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
|
registerCommand(SHyprCtlCommand{"switchxkblayout", false, switchXKBLayoutRequest});
|
||||||
|
|
@ -2114,10 +2189,6 @@ std::string CHyprCtl::getReply(std::string request) {
|
||||||
g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs
|
g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs
|
||||||
g_pInputManager->setTabletConfigs(); // update tablets
|
g_pInputManager->setTabletConfigs(); // update tablets
|
||||||
|
|
||||||
static auto PLAYOUT = CConfigValue<std::string>("general:layout");
|
|
||||||
|
|
||||||
g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout
|
|
||||||
|
|
||||||
g_pHyprOpenGL->m_reloadScreenShader = true;
|
g_pHyprOpenGL->m_reloadScreenShader = true;
|
||||||
|
|
||||||
for (auto& [m, rd] : g_pHyprOpenGL->m_monitorRenderResources) {
|
for (auto& [m, rd] : g_pHyprOpenGL->m_monitorRenderResources) {
|
||||||
|
|
@ -2128,13 +2199,20 @@ std::string CHyprCtl::getReply(std::string request) {
|
||||||
if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible())
|
if (!w->m_isMapped || !w->m_workspace || !w->m_workspace->isVisible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
w->updateDynamicRules();
|
Desktop::Rule::ruleEngine()->updateAllRules();
|
||||||
g_pCompositor->updateWindowAnimatedDecorationValues(w);
|
}
|
||||||
|
|
||||||
|
for (const auto& ws : g_pCompositor->getWorkspaces()) {
|
||||||
|
if (!ws)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ws->updateWindows();
|
||||||
|
ws->updateWindowData();
|
||||||
|
ws->updateWindowDecos();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto const& m : g_pCompositor->m_monitors) {
|
for (auto const& m : g_pCompositor->m_monitors) {
|
||||||
g_pHyprRenderer->damageMonitor(m);
|
g_pHyprRenderer->damageMonitor(m);
|
||||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2146,30 +2224,52 @@ std::string CHyprCtl::makeDynamicCall(const std::string& input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool successWrite(int fd, const std::string& data, bool needLog = true) {
|
static bool successWrite(int fd, const std::string& data, bool needLog = true) {
|
||||||
if (write(fd, data.c_str(), data.length()) > 0)
|
size_t totalWritten = 0;
|
||||||
return true;
|
size_t remaining = data.length();
|
||||||
|
size_t waitsDone = 0;
|
||||||
|
constexpr const size_t MAX_WAITS = 20; // 2000µs = 2ms
|
||||||
|
|
||||||
if (errno == EAGAIN)
|
while (totalWritten < data.length()) {
|
||||||
return true;
|
ssize_t written = write(fd, data.c_str() + totalWritten, remaining);
|
||||||
|
|
||||||
if (needLog)
|
|
||||||
Debug::log(ERR, "Couldn't write to socket. Error: " + std::string(strerror(errno)));
|
|
||||||
|
|
||||||
|
if (waitsDone > MAX_WAITS) {
|
||||||
|
Log::logger->log(Log::ERR, "Couldn't write to socket. Buffer was full and the client couldn't read in time.");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (written < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
// socket buffer full, wait a bit and retry
|
||||||
|
std::this_thread::sleep_for(std::chrono::microseconds(100));
|
||||||
|
waitsDone++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (needLog)
|
||||||
|
Log::logger->log(Log::ERR, "Couldn't write to socket. Error: {}", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
waitsDone = 0;
|
||||||
|
|
||||||
|
totalWritten += written;
|
||||||
|
remaining -= written;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runWritingDebugLogThread(const int conn) {
|
static void runWritingDebugLogThread(const int conn) {
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
Debug::log(LOG, "In followlog thread, got connection, start writing: {}", conn);
|
Log::logger->log(Log::DEBUG, "In followlog thread, got connection, start writing: {}", conn);
|
||||||
//will be finished, when reading side close connection
|
//will be finished, when reading side close connection
|
||||||
std::thread([conn]() {
|
std::thread([conn]() {
|
||||||
while (Debug::SRollingLogFollow::get().isRunning()) {
|
while (Log::SRollingLogFollow::get().isRunning()) {
|
||||||
if (Debug::SRollingLogFollow::get().isEmpty(conn)) {
|
if (Log::SRollingLogFollow::get().isEmpty(conn)) {
|
||||||
std::this_thread::sleep_for(1000ms);
|
std::this_thread::sleep_for(1000ms);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto line = Debug::SRollingLogFollow::get().getLog(conn);
|
auto line = Log::SRollingLogFollow::get().getLog(conn);
|
||||||
if (!successWrite(conn, line))
|
if (!successWrite(conn, line))
|
||||||
// We cannot write, when connection is closed. So thread will successfully exit by itself
|
// We cannot write, when connection is closed. So thread will successfully exit by itself
|
||||||
break;
|
break;
|
||||||
|
|
@ -2177,7 +2277,7 @@ static void runWritingDebugLogThread(const int conn) {
|
||||||
std::this_thread::sleep_for(100ms);
|
std::this_thread::sleep_for(100ms);
|
||||||
}
|
}
|
||||||
close(conn);
|
close(conn);
|
||||||
Debug::SRollingLogFollow::get().stopFor(conn);
|
Log::SRollingLogFollow::get().stopFor(conn);
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2203,10 +2303,10 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
||||||
CRED_T creds;
|
CRED_T creds;
|
||||||
uint32_t len = sizeof(creds);
|
uint32_t len = sizeof(creds);
|
||||||
if (getsockopt(ACCEPTEDCONNECTION, CRED_LVL, CRED_OPT, &creds, &len) == -1)
|
if (getsockopt(ACCEPTEDCONNECTION, CRED_LVL, CRED_OPT, &creds, &len) == -1)
|
||||||
Debug::log(ERR, "Hyprctl: failed to get peer creds");
|
Log::logger->log(Log::ERR, "Hyprctl: failed to get peer creds");
|
||||||
else {
|
else {
|
||||||
g_pHyprCtl->m_currentRequestParams.pid = creds.CRED_PID;
|
g_pHyprCtl->m_currentRequestParams.pid = creds.CRED_PID;
|
||||||
Debug::log(LOG, "Hyprctl: new connection from pid {}", creds.CRED_PID);
|
Log::logger->log(Log::DEBUG, "Hyprctl: new connection from pid {}", creds.CRED_PID);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -2241,7 +2341,7 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
||||||
try {
|
try {
|
||||||
reply = g_pHyprCtl->getReply(request);
|
reply = g_pHyprCtl->getReply(request);
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
Debug::log(ERR, "Error in request: {}", e.what());
|
Log::logger->log(Log::ERR, "Error in request: {}", e.what());
|
||||||
reply = "Err: " + std::string(e.what());
|
reply = "Err: " + std::string(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2261,10 +2361,10 @@ static int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
|
||||||
successWrite(ACCEPTEDCONNECTION, reply);
|
successWrite(ACCEPTEDCONNECTION, reply);
|
||||||
|
|
||||||
if (isFollowUpRollingLogRequest(request)) {
|
if (isFollowUpRollingLogRequest(request)) {
|
||||||
Debug::log(LOG, "Followup rollinglog request received. Starting thread to write to socket.");
|
Log::logger->log(Log::DEBUG, "Followup rollinglog request received. Starting thread to write to socket.");
|
||||||
Debug::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION);
|
Log::SRollingLogFollow::get().startFor(ACCEPTEDCONNECTION);
|
||||||
runWritingDebugLogThread(ACCEPTEDCONNECTION);
|
runWritingDebugLogThread(ACCEPTEDCONNECTION);
|
||||||
Debug::log(LOG, Debug::SRollingLogFollow::get().debugInfo());
|
Log::logger->log(Log::DEBUG, Log::SRollingLogFollow::get().debugInfo());
|
||||||
} else
|
} else
|
||||||
close(ACCEPTEDCONNECTION);
|
close(ACCEPTEDCONNECTION);
|
||||||
|
|
||||||
|
|
@ -2281,7 +2381,7 @@ void CHyprCtl::startHyprCtlSocket() {
|
||||||
m_socketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};
|
m_socketFD = CFileDescriptor{socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)};
|
||||||
|
|
||||||
if (!m_socketFD.isValid()) {
|
if (!m_socketFD.isValid()) {
|
||||||
Debug::log(ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
|
Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (1) IPC will not work.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2292,14 +2392,14 @@ void CHyprCtl::startHyprCtlSocket() {
|
||||||
snprintf(SERVERADDRESS.sun_path, sizeof(SERVERADDRESS.sun_path), "%s", m_socketPath.c_str());
|
snprintf(SERVERADDRESS.sun_path, sizeof(SERVERADDRESS.sun_path), "%s", m_socketPath.c_str());
|
||||||
|
|
||||||
if (bind(m_socketFD.get(), rc<sockaddr*>(&SERVERADDRESS), SUN_LEN(&SERVERADDRESS)) < 0) {
|
if (bind(m_socketFD.get(), rc<sockaddr*>(&SERVERADDRESS), SUN_LEN(&SERVERADDRESS)) < 0) {
|
||||||
Debug::log(ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
|
Log::logger->log(Log::ERR, "Couldn't start the Hyprland Socket. (2) IPC will not work.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10 max queued.
|
// 10 max queued.
|
||||||
listen(m_socketFD.get(), 10);
|
listen(m_socketFD.get(), 10);
|
||||||
|
|
||||||
Debug::log(LOG, "Hypr socket started at {}", m_socketPath);
|
Log::logger->log(Log::DEBUG, "Hypr socket started at {}", m_socketPath);
|
||||||
|
|
||||||
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_socketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
|
m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_wlEventLoop, m_socketFD.get(), WL_EVENT_READABLE, hyprCtlFDTick, nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include "../helpers/MiscFunctions.hpp"
|
#include "../helpers/MiscFunctions.hpp"
|
||||||
#include "../helpers/defer/Promise.hpp"
|
#include "../helpers/defer/Promise.hpp"
|
||||||
#include "../desktop/Window.hpp"
|
#include "../desktop/view/Window.hpp"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <hyprutils/os/FileDescriptor.hpp>
|
#include <hyprutils/os/FileDescriptor.hpp>
|
||||||
|
|
@ -27,6 +27,7 @@ class CHyprCtl {
|
||||||
struct {
|
struct {
|
||||||
bool all = false;
|
bool all = false;
|
||||||
bool sysInfoConfig = false;
|
bool sysInfoConfig = false;
|
||||||
|
bool isDynamicKeyword = false;
|
||||||
pid_t pid = 0;
|
pid_t pid = 0;
|
||||||
SP<CPromise<std::string>> pendingPromise;
|
SP<CPromise<std::string>> pendingPromise;
|
||||||
} m_currentRequestParams;
|
} m_currentRequestParams;
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@
|
||||||
#include "../render/pass/TexPassElement.hpp"
|
#include "../render/pass/TexPassElement.hpp"
|
||||||
#include "../render/Renderer.hpp"
|
#include "../render/Renderer.hpp"
|
||||||
#include "../managers/animation/AnimationManager.hpp"
|
#include "../managers/animation/AnimationManager.hpp"
|
||||||
|
#include "../desktop/state/FocusState.hpp"
|
||||||
|
|
||||||
CHyprDebugOverlay::CHyprDebugOverlay() {
|
CHyprDebugOverlay::CHyprDebugOverlay() {
|
||||||
m_texture = makeShared<CTexture>();
|
m_texture = g_pHyprRenderer->createTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
void CHyprMonitorDebugOverlay::renderData(PHLMONITOR pMonitor, float durationUs) {
|
||||||
|
|
@ -57,7 +58,7 @@ void CHyprMonitorDebugOverlay::frameData(PHLMONITOR pMonitor) {
|
||||||
m_monitor = pMonitor;
|
m_monitor = pMonitor;
|
||||||
|
|
||||||
// anim data too
|
// anim data too
|
||||||
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : g_pCompositor->m_lastMonitor.lock();
|
const auto PMONITORFORTICKS = g_pHyprRenderer->m_mostHzMonitor ? g_pHyprRenderer->m_mostHzMonitor.lock() : Desktop::focusState()->monitor();
|
||||||
if (PMONITORFORTICKS == pMonitor) {
|
if (PMONITORFORTICKS == pMonitor) {
|
||||||
if (m_lastAnimationTicks.size() > sc<long unsigned int>(PMONITORFORTICKS->m_refreshRate))
|
if (m_lastAnimationTicks.size() > sc<long unsigned int>(PMONITORFORTICKS->m_refreshRate))
|
||||||
m_lastAnimationTicks.pop_front();
|
m_lastAnimationTicks.pop_front();
|
||||||
|
|
@ -258,15 +259,7 @@ void CHyprDebugOverlay::draw() {
|
||||||
cairo_surface_flush(m_cairoSurface);
|
cairo_surface_flush(m_cairoSurface);
|
||||||
|
|
||||||
// copy the data to an OpenGL texture we have
|
// copy the data to an OpenGL texture we have
|
||||||
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
m_texture = g_pHyprRenderer->createTexture(m_cairoSurface);
|
||||||
m_texture->allocate();
|
|
||||||
m_texture->bind();
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
|
||||||
|
|
||||||
CTexPassElement::SRenderData data;
|
CTexPassElement::SRenderData data;
|
||||||
data.tex = m_texture;
|
data.tex = m_texture;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ class CHyprDebugOverlay {
|
||||||
cairo_surface_t* m_cairoSurface = nullptr;
|
cairo_surface_t* m_cairoSurface = nullptr;
|
||||||
cairo_t* m_cairo = nullptr;
|
cairo_t* m_cairo = nullptr;
|
||||||
|
|
||||||
SP<CTexture> m_texture;
|
SP<ITexture> m_texture;
|
||||||
|
|
||||||
friend class CHyprMonitorDebugOverlay;
|
friend class CHyprMonitorDebugOverlay;
|
||||||
friend class CHyprRenderer;
|
friend class CHyprRenderer;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
||||||
#include "../Compositor.hpp"
|
#include "../Compositor.hpp"
|
||||||
#include "../config/ConfigValue.hpp"
|
#include "../config/ConfigValue.hpp"
|
||||||
#include "../render/pass/TexPassElement.hpp"
|
#include "../render/pass/TexPassElement.hpp"
|
||||||
|
#include "../event/EventBus.hpp"
|
||||||
|
|
||||||
#include "../managers/animation/AnimationManager.hpp"
|
#include "../managers/animation/AnimationManager.hpp"
|
||||||
#include "../managers/HookSystemManager.hpp"
|
|
||||||
#include "../render/Renderer.hpp"
|
#include "../render/Renderer.hpp"
|
||||||
|
|
||||||
static inline auto iconBackendFromLayout(PangoLayout* layout) {
|
static inline auto iconBackendFromLayout(PangoLayout* layout) {
|
||||||
|
|
@ -22,14 +22,12 @@ static inline auto iconBackendFromLayout(PangoLayout* layout) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CHyprNotificationOverlay::CHyprNotificationOverlay() {
|
CHyprNotificationOverlay::CHyprNotificationOverlay() {
|
||||||
static auto P = g_pHookSystem->hookDynamic("focusedMon", [&](void* self, SCallbackInfo& info, std::any param) {
|
static auto P = Event::bus()->m_events.monitor.focused.listen([&](PHLMONITOR mon) {
|
||||||
if (m_notifications.empty())
|
if (m_notifications.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
g_pHyprRenderer->damageBox(m_lastDamage);
|
g_pHyprRenderer->damageBox(m_lastDamage);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_texture = makeShared<CTexture>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
|
CHyprNotificationOverlay::~CHyprNotificationOverlay() {
|
||||||
|
|
@ -232,16 +230,7 @@ void CHyprNotificationOverlay::draw(PHLMONITOR pMonitor) {
|
||||||
|
|
||||||
m_lastDamage = damage;
|
m_lastDamage = damage;
|
||||||
|
|
||||||
// copy the data to an OpenGL texture we have
|
m_texture = g_pHyprRenderer->createTexture(m_cairoSurface);
|
||||||
const auto DATA = cairo_image_surface_get_data(m_cairoSurface);
|
|
||||||
m_texture->allocate();
|
|
||||||
m_texture->bind();
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_R, GL_BLUE);
|
|
||||||
m_texture->setTexParameter(GL_TEXTURE_SWIZZLE_B, GL_RED);
|
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA);
|
|
||||||
|
|
||||||
CTexPassElement::SRenderData data;
|
CTexPassElement::SRenderData data;
|
||||||
data.tex = m_texture;
|
data.tex = m_texture;
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue